模板:扩展KMP,用extend[i]保存 主串 S[i.....n-1]与 模式串 T的最长公共前缀的长度
using namespace std;
int ne[maxn],extend[maxn];
void EKMP(char s[],char t[])//s[]为主串,t[]为模版串
{
int i,j,p,l;
int len=strlen(t);
int len1=strlen(s);
memset(ne,0,sizeof(ne));
memset(extend,0,sizeof(extend));
ne[0]=len;
j=0;
while(1+j<len&&t[j]==t[1+j])j++;
ne[1]=j;
int a=1;
for(i=2; i<len; i++)
{
p=ne[a]+a-1;
l=ne[i-a];
if(i+l<p+1)ne[i]=l;
else
{
j=max(0,p-i+1);
while(i+j<len&&t[i+j]==t[0+j])j++;
ne[i]=j;
a=i;
}
}
j=0;
while(j<len1&&j<len&&s[j]==t[j])j++;
extend[0]=j;
a=0;
for(i=1; i<len1; i++)
{
p=extend[a]+a-1;
l=ne[i-a];
if(l+i<p+1)extend[i]=ne[i-a];
else
{
j=max(0,p-i+1);
while(i+j<len1&&j<len&&s[i+j]==t[j])j++;
extend[i]=j;
a=i;
}
}
}
int main().
{
char a[maxn],b[maxn];
while(~scanf("%s%s",a,b)){
EKMP(a,b);
for(int i=0;i<strlen(a);i++)cout<<extend[i]<<" ";
}
return 0;
}
const int maxn=100010; //字符串长度最大值
int next[maxn],ex[maxn]; //ex数组即为extend数组
//预处理计算next数组
void GETNEXT(char *str)
{
int i=0,j,po,len=strlen(str);
next[0]=len;//初始化next[0]
while(str[i]==str[i+1]&&i+1<len)//计算next[1]
i++;
next[1]=i;
po=1;//初始化po的位置
for(i=2;i<len;i++)
{
if(next[i-po]+i<next[po]+po)//第一种情况,可以直接得到next[i]的值
next[i]=next[i-po];
else//第二种情况,要继续匹配才能得到next[i]的值
{
j=next[po]+po-i;
if(j<0)j=0;//如果i>po+next[po],则要从头开始匹配
while(i+j<len&&str[j]==str[j+i])//计算next[i]
j++;
next[i]=j;
po=i;//更新po的位置
}
}
}
//计算extend数组
void EXKMP(char *s1,char *s2)
{
int i=0,j,po,len=strlen(s1),l2=strlen(s2);
GETNEXT(s2);//计算子串的next数组
while(s1[i]==s2[i]&&i<l2&&i<len)//计算ex[0]
i++;
ex[0]=i;
po=0;//初始化po的位置
for(i=1;i<len;i++)
{
if(next[i-po]+i<ex[po]+po)//第一种情况,直接可以得到ex[i]的值
ex[i]=next[i-po];
else//第二种情况,要继续匹配才能得到ex[i]的值
{
j=ex[po]+po-i;
if(j<0)j=0;//如果i>ex[po]+po则要从头开始匹配
while(i+j<len&&j<l2&&s1[j+i]==s2[j])//计算ex[i]
j++;
ex[i]=j;
po=i;//更新po的位置
}
}
}
hdu4333
给一个数字字符串S, 可以把S最后一个数字移动到最前面变成另一个数字。例如123, 经过移动依次变成312,231,123。 注意当移动次数正好和S长度相等时,S又变回了最开始的那个数字。
求这个移动过程所形成的所有字符串,大于S(最初的)的数字,等于S,以及小于S的各有多少个。
扩展KMP能求出一个串所有后缀串(即s[i...len])和模式串的最长公共前缀。于是只要将这个串复制一遍,求出该串每个后缀与其本身的最长公共前缀即可,当公共前缀>=len时,显然相等,否则只要比较下一位就能确定这个串与原串的大小关系。
至于重复串的问题,只有当这个串有循环节的时候才会产生重复串,用KMP的next数组求出最小循环节,用长度除以最小循环节得到循环节个数,在将3个答案都除以循环节个数即可。
#include<cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>
#define maxn 100005
#define LL long long
#define MS(a,b) memset(a,b,sizeof(a))
#define FI(a,b) fill(a,a+maxn,b)
#define sf(n) scanf("%d",&n)
#define sf2(a,b) scanf("%d%d",&a,&b)
#define pf(n) printf("%d\n",n)
#define ffr(i,n) for(i=0;i<n;i++)
/*
s:abcdabcadcabcdcab
p: abcdcab
next:-1000001
*/
using namespace std;
int ne[maxn],extend[maxn],m,l,r,knext[maxn];
void EKMP(char s[],char t[])//s[]为主串,t[]为模版串
{
int i,j,p,l;
int len=strlen(t);
int len1=strlen(s);
memset(ne,0,sizeof(ne));
memset(extend,0,sizeof(extend));
ne[0]=len;
j=0;
while(1+j<len&&t[j]==t[1+j])j++;
ne[1]=j;
int a=1;
for(i=2; i<len; i++)
{
p=ne[a]+a-1;
l=ne[i-a];
if(i+l<p+1)ne[i]=l;
else
{
j=max(0,p-i+1);
while(i+j<len&&t[i+j]==t[0+j])j++;
ne[i]=j;
a=i;
}
}
j=0;
while(j<len1&&j<len&&s[j]==t[j])j++;
extend[0]=j;
a=0;
for(i=1; i<len1; i++)
{
p=extend[a]+a-1;
l=ne[i-a];
if(l+i<p+1)extend[i]=ne[i-a];
else
{
j=max(0,p-i+1);
while(i+j<len1&&j<len&&s[i+j]==t[j])j++;
extend[i]=j;
a=i;
}
}
}
void Getnxt(char *S){
knext[0]=-1;
int i=1,j=0;
int len=strlen(S);
while(i<len){
if(S[i]==S[j]||j==-1){
i++;
j++;
knext[i]=j;
}else{
j=knext[j];
}
}
}
int main()
{
char a[maxn*2],b[maxn];
int Case=1,t,i,j;
sf(t);
while(t--){
scanf("%s",a);
strcpy(b,a);
strcat(a,b);
//cout<<a<<endl;
EKMP(a,b);
int len=strlen(b);
m=0;r=0;l=0;
for(i=0;i<len;i++){
j=extend[i];
if(j>=len)m++;
else if(b[j]>a[i+j])l++;
else if(b[j]<a[i+j])r++;
//cout<<b[]
}
Getnxt(b);
int qt=1;
int t=len-knext[len];
if(len%t==0){
qt=len/t;//求出循环节;
}
printf("Case %d: %d %d %d\n",Case++,l/qt,m/qt,r/qt);
}
return 0;
}