• 字符串相关


    补一些字符串姿势。

    一: 最小表示法 {

      初始时,让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组,则每一组是一个等差数列。

  • 相关阅读:
    [Jenkins] 批量删除构建历史
    [Android Tips] 32. 解决 Android Device Monitor 在 Mac OS X 卡住
    [Gradle] 查看项目依赖
    [Gradle] 获取 gradle 命令行参数
    [Android Tips] 31.如何将第三库引入的 Permission 删除掉
    [Gradle] 发布构件到本地仓库
    [Android Tips] 30.如何在 Android Studio 中一次性格式化所有代码
    下载安卓应用的历史版本
    设置int、float型数据的输出格式
    头文件limits—各个类型的数据的范围
  • 原文地址:https://www.cnblogs.com/cyz666/p/8562431.html
Copyright © 2020-2023  润新知