字符串相关

补一些字符串姿势。

一: 最小表示法 {

  初始时,让i=0,j=1,k=0,其中i,j,k表示的是以i开头和以j开头的字符串的前k个字符相同

  分为三种情况

  1.如果str[i+k]==str[j+k] k++。

  2.如果str[i+k] > str[j+k] i = i + k + 1,即最小表示不可能以str[i->i+k]开头。

  3.如果str[i+k] < str[j+k] j = j + k + 1,即最小表示不可能以str[j->j+k]开头。

  那么只要循环n次,就能够判断出字符串的最小表示是以哪个字符开头。

 1 int getMin(){
 2     int n=strlen(S);
 3     int i=0,j=1,k=0;
 4     while (i<n&&j<n&&k<n){
 5         int t=S[(i+k)%n]-S[(j+k)%n];
 6         if (t==0) k++;
 7         else{
 8             if (t>0) i+=k+1;
 9             else j+=k+1;
10             if (i==j) j++;
11             k=0;
12         }
13     }
14     return i<j?i:j;
15 }
里の风景

}

二:KMP {

1 void getkmp(char S[]){
2     int n=strlen(S+1);
3     nex[0]=nex[1]=0;
4     for (int i=2,j;i<=n;++i){
5         for (j=nex[i-1];j&&S[i]!=S[j+1];j=nex[j]);
6         if (S[i]!=S[j+1]) nex[i]=0; else nex[i]=j+1;
7     }
8 }
神様

}

三:AC自动机 {

 1 //rt is 0
 2 l=r=1;
 3 for (int i=0;i<26;++i) if (ch[0][i]) nex[d[r++]=ch[0][i]]=0;
 4 while (l<r){
 5     for (int i=0;i<26;++i)
 6     if (ch[d[l]][i]){
 7         d[r++]=ch[d[l]][i];
 8         nex[ch[d[l]][i]]=ch[nex[d[l]]][i];
 9     }else ch[d[l]][i]=ch[nex[d[l]]][i];
10     ++l;
11 }
前前世世

}

四:后缀自动机(SAM) {

  转到http://www.cnblogs.com/cyz666/p/6527196.html

}

五:后缀数组(SA) {

 1 #include <bits/stdc++.H>
 2 using namespace std;
 3 int n,m,F0[27],rk[200005],sa[200005],H[200005],g1[200005],g2[200005],nex[200005];
 4 char S[200005];
 5 int main(){
 6     scanf("%s",S+1); n=strlen(S+1);
 7     for (int i=1;i<=n;++i) ++F0[S[i]-'a'+1];
 8     for (int i=2;i<=26;++i) F0[i]+=F0[i-1];
 9     for (int i=1;i<=n;++i) rk[i]=F0[S[i]-'a'+1];
10     for (int m=1,t=F0[26];m<n;m<<=1){
11         for (int i=1;i<=n;++i){
12             nex[i]=g1[rk[i+m]];
13             g1[rk[i+m]]=i;
14         }
15         for (int i=t;i>=0;--i){
16             for (int j=g1[i],k;j;j=k){
17                 k=nex[j]; nex[j]=g2[rk[j]]; g2[rk[j]]=j;
18             }
19             g1[i]=0;
20         }
21         int z=0,y;
22         for (int i=1;i<=t;++i){
23             y=-1;
24             for (int j=g2[i],k;j;j=k){
25                 k=nex[j]; nex[j]=0;
26                 if (y!=rk[j+m]) y=rk[j+m],++z;
27                 H[j]=z;
28             }
29             g2[i]=0;
30         }
31         t=z;
32         for (int i=1;i<=n;++i) rk[i]=H[i];
33     }
34     for (int i=1;i<=n;++i) sa[rk[i]]=i,H[i]=0;
35     for (int i=1,k=0;i<=n;++i)
36     if (rk[i]!=1){
37         if (k) --k;
38         while (S[i+k]==S[sa[rk[i]-1]+k]) ++k;
39         H[rk[i]]=k;
40     }
41     /*for (int i=1;i<=n;++i){
42         printf("%d ",H[i]);
43         for (int j=sa[i];j<=n;++j) putchar(S[j]); puts("");
44     }*/
45 }
古树旋律

}

六:Manacher {

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 char S[200005];
 4 int n,x,a[400005],f[400005];
 5 int main(){
 6     scanf("%s",S+1); n=strlen(S+1);
 7     for (int i=1;i<=n;++i) a[i*2]=S[i]-'a'+1;
 8     x=0;
 9     for (int i=1,j;i<=n*2+1;++i){
10         if (i<=x+f[x]) j=min(f[x-i+x],x+f[x]-i); else j=0;
11         for (++j;i+j<=n*2-1&&i-j>0&&a[i+j]==a[i-j];++j);
12         f[i]=j-1;
13         if (f[i]+i>f[x]+x) x=i;
14     }
15     for (int i=1;i<=n*2+1;++i) printf("%d ",a[i]); puts("");
16     for (int i=1;i<=n*2+1;++i) printf("%d ",f[i]); puts("");
17     return 0;
18 }
殻ノ少女

}

七:回文自动机 {

  

}

八:一些结论

  1.border。

    串A,串B  若串S=AB=BA则,可以得出gcd(|A|,|B|)是S的一个循环节。

    由此可得引理,对S做kmp后,  若n-nex[n]整除n则 串S是最小循环节为n-nex[n] 否则 串S不是循环串

  2.候选最小后缀  (这里的最小指字典序,空集字典序最小)。

    定义:串S的一个后缀A是候选最小后缀(以下简称"小后缀"  与最小后缀区分) ,则存在字符串T使得 AT是ST的最小后缀

    如aaba中   aaba和a 是小后缀。

    可以证明,两个小后缀A,B  (|A|<|B|)  一定满足A是B前缀,且|A|*2<=|B|。 因此可以证明 串S最多有log(len)个小后缀。

    所以可以用线段树维护区间字符串的某些最小字典序问题。。因为 最多只要维护区间的log个。

   3.回文串

    回文串S的后缀T是回文串,当且仅当T是S的border。

    回文串的所有border都是回文串

  4.循环同构

    串S与T循环同构,当且仅当S是TT的子串。(S,T长度当然要相同)

    若A,B都是自身的循环同构中字典序最小,且A<B,则AB是循环同构最小的

  5.周期

    串S有两个周期p,q。  gcd(p,q)不一定是串S的周期,反例:abaaba。

    Periodicity Lemma:当且仅当p+q-gcd(p,q)<=|S| 时,   gcd(p,q)也是串S的周期 

  6.匹配与等差数列

    若2|u|>=|v|,则串u在v中的所有匹配构成等差数列。

    若匹配的个数>=3个,则公差为u的最小周期。

    (若只有2个 则不一定是最小的周期, 比如:u=aabaa  , v=aabaaabaa)

  7.border与等差数列

    将S的所有border按长度 分成 [1,2),[2,4),[4,8),...,[2^(i-1) 2^i) , [2^i  n)

    log组,则每一组是一个等差数列。

posted @ 2018-03-13 21:25  cyz666  阅读(288)  评论(0编辑  收藏  举报