一、暴力匹配
贴上自己的代码,AC.
#include<stdio.h>
#include<string.h>
int main()
{
char source[]="fiuhiuhrfeihgiuehjiuvnuifdhn";
char pattern[]="hrfeih";
int n=strlen(source);
int m=strlen(pattern);
printf("%d\n",n);
printf("%d\n",m);
int i,j;
for(i=0;i<=n-m;i++){
for(j=0;j<m;j++){
if(pattern[j]!=source[i+j])
break;
}
if(m == j){
printf("%d",i);
break;
}
}
if(i==n-m+1)
printf("%d",-1);
return 0;
}
二、Rabin-Karp算法
参考资料:http://www.ituring.com.cn/article/1759#
代码参考:http://blog.chinaunix.net/uid-26548237-id-3968132.html
三、KMP基础知识
KMP算法思想复杂,但实现却极其简单,已整理于笔记上。
参考资料:
http://chaoswork.com/blog/2011/06/14/kmp%E7%AE%97%E6%B3%95%E5%B0%8F%E7%BB%93/(最主要、最精彩的一篇)
http://blog.csdn.net/yutianzuijin/article/details/11954939
四、KMP应用
1. POJ2406:http://poj.org/problem?id=2406
以模式串ababab为例
a b a b a b (虚位以待)
next: – 1 0 0 1 2 3 4 (虚位以待)
next[0] next[1] next[2] …… next[6] (虚位以待)
注意:这里的next数组的值不同于KMP所用的next数组,区别在于:
a b a b a b
KMP_next: -1 0 0 1 2 3
next[0] next[1] next[2] …… next[5]
都是从next[1]开始计算,但是KMP_next不进行最后一个字符的比较工作(即真正有效的是前面5个字符,s[0]~s[4]),而next进行(全部的六个字符有效,s[0]~s[5]);最后一个字符之前的工作是完全一样的,显然可通过循环次数来控制这两种情况下next数组的获取(当i=4时,对于KMP_next来说,进行的是最后一次循环,此时比较s[4]与s[2],得到next[5]的值,(i=5)退出循环,但是next却仍有一次循环(因为此时i=5<6),此时比较s[5]与s[3],得到next[6]的值)。
代码如下:
void KMP_Next(char* p,int next[])
{
int pLen = strlen(p);
next[0] = -1;
int j = -1;
int i = 0;
while ( i < pLen - 1) //注意KMP的next数组就在这里不同!
{
if (j == -1 || p[i] == p[j])
{
++i;
++j;
next[i] = j;
}
else
{
j = next[j];
}
}
}
回到本题,分析如下:strlen(dst) = 6 ,但前缀、后缀的最长相等长度却等于4,按理来说模式串的长度等于4 × 2 = 8 ,才会使得恰好不存在中间的循环串(此时恰好是2个相等的字符串重复出现),但现在总长度小于8,说明模式串中间是存在重复串的,这种情况下重复出现的字符串的个数肯定是大于2的,
循环节的长度:设strlen(dst) = n , 则循环节的长度 = n – next [ n ] 。原因:next[6] =4 , 必然有next [4] = 2,←←←←s[0 1 2 3] = s [2 3 4 5],则s[0 1] = s [2 3] ,显然next [4] = 2。如果继续往下推,则有s[4 5] = s [2 3],则循环节的长度为 s[4 5] 的长度2 ,也就是
重叠部分同时作为前缀的后半部分和后缀的前半部分。
模式串长度除去最长相同前缀后缀长度,剩下的长度就是循环串的长度。
除了上面的那种以strlen(dst) < = 2× 前缀、后缀的最长相等长度来判断是否存在循环串以外,还有一种方法:
n % 循环串的长度 ? = 0 ,如果 = 0,说明存在循环串,循环串的个数等于 n / 循环串的长度;如果 != 0 ,说明在某些位置夹杂着其他不属于循环串的字符(这是显而易见的!)
代码如下:
#include<iostream>
#include<string.h>
using namespace std;
int next[1000005];
char s[1000005];
void getnext( ) //这样得到的next数组是符合预期的(不同于KMP_next数组),利用了字符串(也是next的下标)长度比最大(字符串)下标值多1的特性
{
int i=0,j=-1;
next[0]=-1;
int len=strlen(s);
while(i<len)
{
if( j==-1 || s[i]==s[j] )
{
i++;
j++;
next[i]=j;
}
else
j=next[j];
}
}
int main()
{
while(scanf("%s",s)>0)
{
if(s[0]=='.')
break;
int len=strlen(s);
getnext();
if(len%(len-next[len])==0)
printf("%d\n",len/(len-next[len]));
else
printf("1\n");
}
return 0;
}
参考资料:http://www.cnblogs.com/ziyi–caolu/archive/2013/01/01/2841708.html
http://www.cnblogs.com/coredux/archive/2012/08/13/2635987.html
2. POJ 1961:http://poj.org/problem?id=1961
给你一个字符串,求这个字符串到第i个字符为止的循环节的次数。
比如aabaabaabaab,长度为12.到第二个a时,a出现2次,输出2.到第二个b时,aab出现了2次,输出2.到第三个b时,aab出现3次,输出3.到第四个b时,aab出现4次,输出4.
上一题是输出一个字符串的循环节出现的次数,这个是到第i个字符为止,其实就是多了一层循环——把这个字符串遍历一次即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
using namespace std;
#define N 1000010
char s[N];
int nextval[N];
int len;
void getnext(const char *s)
{
int i = 0, j = -1;
next[0] = -1;
while(i != len)
{
if(j == -1 || s[i] == s[j])
next[++i] = ++j;
else
j = next[j];
}
}
int main()
{
int T = 1;
int length, add;
while(scanf("%d", &len) && len)
{
scanf("%s", s);
getnext(s);
printf("Test case #%d\n", T++);
for(int i = 1; i <= len; ++i)
{
length = i - next[i]; //循环节的长度
if(i != length && i % length == 0) //如果有多个循环串(不止1个)
printf("%d %d\n", i, i / length);
}
printf("\n");
}
return 0;
}
参考资料:http://blog.csdn.net/niushuai666/article/details/6967716