扩展KMP算法
擴展KMP的應用:
?
給出模板串S和串T,長度分別為Slen和Tlen,要求在線性時間內,對于每個S[i](0<=i<Slen),求出S[i..Slen-1]與T的
最長公共前綴長度,記為extend[i](或者說,extend[i]為滿足S[i..i+z-1]==T[0..z-1]的最大的z值)。
擴展KMP可以用來解決很多字符串問題,如求一個字符串的最長回文子串和最長重復子串。
?
題目:http://acm.hdu.edu.cn/showproblem.php?pid=4333
?
題意:給一個數字,每一次把它的最后一位拿到最前面,一直那樣下去,分別求形成的數字小于,等于和大于原來數的個數。
例如:134可以形成134,341,413三個數,所以分別是1,1,1。
?
分析:
由于長度為len的字符串形成題目要求的串的個數為len,那么我們可以把原來的兩個串T連接起來形成字符串S,然后找S的每
個后綴的前len個元素即可。
?
這里主要是如何比較的問題,對于字符串的比較,我們可以先求出他們的最長公共前綴長度,然后只需要比較一次就可以知道結果了。那么最長公共前綴怎么求,由于這里是一個串T與另一個串S,來求S的所有后綴與T的最長公共前綴長度,所以用擴展
KMP。如果extend[i]>=len,就說明與原來的相等了,否則如果S[i+extend[i]]<T[extend[i]]就說明小于,否則就是大
于。
?
#include <stdio.h> #include <string.h> #define N 500010int next[N]; int nextval[N]; int extend[N];char S[N]; char T[N];void GetNext(char *T) {int a=0;int Tlen=strlen(T);next[0]=Tlen;while(a<Tlen-1&&T[a]==T[a+1]) a++;next[1]=a;a=1;for(int k=2;k<Tlen;k++){int p=a+next[a]-1,L=next[k-a];if((k-1)+L>=p){int j=(p-k+1)>0? p-k+1:0;while(k+j<Tlen&&T[k+j]==T[j]) j++;next[k]=j;a=k;}else next[k]=L;} }void GetExtend(char *S,char *T) {int a=0;GetNext(T);int Slen=strlen(S);int Tlen=strlen(T);int MinLen=Slen<Tlen? Slen:Tlen;while(a<MinLen&&S[a]==T[a]) a++;extend[0]=a;a=0;for(int k=1;k<Slen;k++){int p=a+extend[a]-1,L=next[k-a];if((k-1)+L>=p){int j=(p-k+1)>0? p-k+1:0;while(k+j<Slen&&j<Tlen&&S[k+j]==T[j]) j++;extend[k]=j;a=k;}else extend[k]=L;} }void NextVal(char *T) {int i=0,j=-1;nextval[0]=-1;int Tlen=strlen(T);while(i<Tlen){if(j==-1||T[i]==T[j]){i++;j++;if(T[i]!=T[j]) nextval[i]=j;else nextval[i]=nextval[j];}else j=nextval[j];} }int main() {int Slen,Tlen,i;int t,tt=1;scanf("%d",&t);while(t--){scanf("%s",S);strcpy(T,S);strcat(S,T);GetExtend(S,T);Tlen=strlen(T);Slen=strlen(S);NextVal(T);int MOD=Tlen-nextval[Tlen];int temp=1;if(Tlen%MOD==0) temp=Tlen/MOD;int ans1=0,ans2=0,ans3=0;for(i=0;i<Tlen;i++){if(extend[i]>=Tlen) ans2++;else if(S[i+extend[i]]<T[extend[i]]) ans1++;else ans3++;}printf("Case %d: %d %d %d\n",tt++,ans1/temp,ans2/temp,ans3/temp);}return 0; }
?
?
總結
- 上一篇: 经典搜索题
- 下一篇: 离散化+树状数组求逆序数