菜鸟就要老老实实重新学起:
KMP
字符串kmp算法,就是前缀匹配,计算要查找的字符串的next[ ]值,就是该字符串每个位置字符匹配错误时的跳转位置,从而使字符串匹配复杂度由O(M*N)降到O(M+N);
下面模版将基础的计算next值的方法加了一层回滚,当该处与回滚处字符相同,则该处的next值继续向前回滚到字符不同为止;
next[] 可以用来计算循环节,i % (i-next[i]) == 0 ; 时 i-next[i] 就是循环节的长度;(poj 1960 hdu 3374)
模版:
void getnext(char *pre, int len, int *next)
{
int i = 0,j = -1;
next[0] = -1;
while(i < len)
{
if(j == -1 || pre[i] == pre[j])
{
i++;
j++;
//简单写法
// next[i] = j;
//当该处与回滚处字符相同,则该处的next值继续向前回滚到字符不同为止;
if(pre[i]!=pre[j]) next[i] = j;
else next[i] = next [j];
}
else
j = next[j];
}
}
int kmp(char *str, char *pre, int pos)
{
int lenp = strlen(pre), lens = strlen(str);
int next[lenp];
getnext(pre, lenp, next);
int i,j;
i = pos;
j = 0;
while(i < lens && j < lenp)
{
if(j == -1 || str[i] == pre[j])
{
i++;
j++;
}
else
j = next[j];
// if(j == lenp) sum++;//记数(去掉while()中的j<lenp);
}
if(j >= lenp)
return i - lenp;
else
return -1;
}
eg:
POJ1961 Period
http://poj.org/problem?id=1961
题意:
求该字符串到第i个字符为止的循环节循环的次数
思路:
用kmp,计算next[]标记回滚位置,则 i 到回滚处next[i] 的距离即为循环节长度,可以整除时就是完整循环的字符串;
code:
#define N 1123456
int n,m;
char s[N];
int next[N];
void getnext(char *pre, int len)
{
int i=0,j=-1;
next[0] = -1;
while(i < len)
{
if(j == -1 || pre[i] == pre[j])
{
i++,j++;
next[i] = j;
}
else
j = next[j];
}
}
int main()
{
int i,j,k,kk,t,x,y;
k=0;
while(scanf("%d",&n)!=EOF&&n)
{
scanf("%s",s);
getnext(s,n);
printf("Test case #%d\n",++k);
for(i=1;i<=n;i++)
{
t = i-next[i];
if(i!=t && (i)%t==0)
printf("%d %d\n",i,i/t);
}
printf("\n");
}
return 0;
}
POJ2752 Seek the Name, Seek the Fame
http://poj.org/problem?id=2752
题意:
求所有即使前缀又是后缀的字符串长度
思路:
用kmp,计算next[]标记回滚位置,既是前缀又是后缀一定最后一个字符与字符串最后一个字符相等,
从最后一个字符向前回滚,记录所有位置长度;
code:
#define N 1123456
int n,m;
int a[N],b[N];
char s[N];
int next[N];
void getnext(char *pre, int len)
{
int i=0,j=-1;
next[0] = -1;
while(i < len)
{
if(j == -1 || pre[i] == pre[j])
{
i++,j++;
next[i] = j;
}
else
j = next[j];
}
}
int main()
{
int i,j,k,kk,t,x,y;
k=0;
while(scanf("%s",s)!=EOF)
{
n = strlen(s);
getnext(s,n);
i=n-1;
j=0;
while(i>-1)
{
if(s[i]==s[n-1])
a[j++] = i+1;
i = next[i];
}
for(j--;j>=0;j--)
printf("%d ",a[j]);
printf("\n");
}
return 0;
}
HDU3374 String Problem
http://acm.hdu.edu.cn/showproblem.php?pid=3374
题意:
一个字符串可以把首字符置于最后,共形成len个字符串,找出这些字符串中的字典序最小最大串的序号及个数;
思路:
用kmp的next[] 求循环节就是最大最小字符串的数量;
然后求最大最小字符串,用两个指针i,j 对比指向的字符,相等就一同向后偏移,不等就保持小的把大的后移到偏移处,
最终找出最小串的位置,最大串同理;
code:
#include <bits/stdc++.h>
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define EPS 1e-6
#define N 1123456
using namespace std;
int n,m,sum,res,flag;
char s[N];
void getNext(char *pre, int len, int *next)
{
int i = 0,j = -1;
next[0] = -1;
while(i < len)
{
if(j == -1 || pre[i] == pre[j])
{
i++,j++;
next[i] = j;
}
else j = next[j];
}
}
int minString(char *s)
{
int i=0,j=1,k=0;
int len=strlen(s);
while(i<len&&j<len&&k<len)
{
if(s[(i+k)%len]==s[(j+k)%len])k++;
else
{
if(s[(i+k)%len]>s[(j+k)%len])i=i+k+1;
else j=j+k+1;
if(i==j)j++;
k=0;
}
}
return i<j?i:j;
}
int maxString(char *s)
{
int i=0,j=1,k=0;
int len=strlen(s);
while(i<len&&j<len&&k<len)
{
if(s[(i+k)%len]==s[(j+k)%len])k++;
else
{
if(s[(i+k)%len]<s[(j+k)%len])i=i+k+1;
else j=j+k+1;
if(i==j)j++;
k=0;
}
}
return i<j?i:j;
}
int main()
{
int i,j,k,kk,cas,T,t,x,y,z;
while(scanf("%s",&s)!=EOF)
{
n=strlen(s);
int next[n+1];
getNext(s,n,next);
res=n-next[n];
printf("%d %d %d %d\n",minString(s)+1,n/res,maxString(s)+1,n/res);
}
return 0;
}