zoukankan      html  css  js  c++  java
  • 字符串系列复习

    AC自动机

    Trie+Fail指针,通常配合动归出现在比较明显的字符串题中。

    几个注意点:

    1.bfs建图时先将所有fail指针指向根(0号节点),一开始应将根的所有儿子放入队列而不是根本身。

    2.模板使用的方法是建立trie图而不是树,注意处理。

    3.fail指针构成一棵树(显然),解题时常利用这个性质。

    4.直接按照trie的方法建树,然后按bfs序建图。

    5.常用于多串匹配,技巧是现考虑暴力再考虑用自动机性质优化复杂度。

    void ins(char s[]){
        int l=strlen(s),x=0;
        rep(i,0,l-1){
            int t=s[i]-'A'+1;
            if (!ch[x][t]) clear(++cnt),ch[x][t]=cnt;
            x=ch[x][t];
        }
        w[x]=1;
    }
    
    void build(){
        int st=1,ed=0;
        rep(i,1,26) if (ch[0][i]) q[++ed]=ch[0][i];
        while (st!=ed+1){
            int x=q[st++];
            rep(i,1,26)
                if (ch[x][i]) q[++ed]=ch[x][i],fail[ch[x][i]]=ch[fail[x]][i];
                            else ch[x][i]=ch[fail[x]][i];
            w[x]|=w[fail[x]];
        }
    }

    后缀自动机

    http://blog.csdn.net/doyouseeman/article/details/52245413

    相当于将一个串的所有后缀放入一个trie然后将几个出现位置(右断点)相同的串压缩到同一个节点

    稍难理解,注意点:

    1.之所以能处理子串问题,本质是利用了“子串就是某个后缀的前缀”

    2.一个节点存储几个Right集合相同的串(一定互为后缀),但也可以认为它只表示一个串(从根走到这个节点就相当于只走到这一个串上)

    3.x代表的所有串后接一个c之后的Right集合与son[x][c]Right集合完全相同,而fa[x]代表的集合包含x代表的集合(且是最小的包含x代表的集合的节点)。

    4.从根发出所有路径构成了原串的所有子串。每个节点代表子串的长度范围是(  mx[fa[x]]  ,  mx[x]  ]。

    5.走son寻找子串,走fa传递信息。两节点代表的集合若非包含关系则必然不相交。

    6.两个前缀的最长公共后缀,位于这两个前缀对应状态在parent树上的LCA

    主要应用于大量子串处理问题上,也可配合map等骗分。

    应用(转载自http://blog.csdn.net/w305172521/article/details/50461200

    存在性查询 

    问题:给定文本T,询问格式如下:给定字符串P,问P是否是T的子串。 
    算法:我们对文本T用O(length(T))建立后缀自动机。 
    解释:从s状态开始走,沿着串p的字符开始走,如果能够走通则是子串,否则不是

    不同子串个数 
    问题:给定字符串S,问它有多少不同的子串。 
    算法:构建S后缀自动机,O(n) 
    解释:S的任意子串都对应SAM中的一条路径,所以路径条数就是子串个数

    不同子串的总长 
    问题:给定字符串S,求其所有不同子串的总长度。 
    算法:上题类似,加个长度

    字典序第k小子串 
    问题:给定字符串S,一系列询问——给出整数K_i,计算S的所有子串排序后的第K_i个。 
    复杂度要求:单次询问O(length(ans)*Alphabet),其中ans是该询问的答案,Alphabet是字母表大小。 
    算法:这一问题的基础思路和上两题类似。字典序第k小子串——自动机中字典序第k小的路径。因此,考虑从每个状态出发的不同路径数,我们将得以轻松地确定第k小路径,从初始状态开始逐位确定答案。

    最小循环移位 
    问题.给定字符串S,找到和它循环同构的字典序最小字符串。

    出现次数查询 
    问题.给定文本T,询问格式如下:给定字符串P,希望找出P作为子串在文本T中出现了多少次(出现区间可以相交)。

    首次出现位置查询 
    问题.给定文本T,询问格式如下:给定字符串P,求P在文本中第一次出现的位置。

    所有出现位置查询 
    问题.给定文本T,询问格式如下:给定字符串P,要求给出P在T中的所有出现位置(出现区间可以相交)。

    查询不在文本中出现的最短字符串 
    问题.给定字符串S和字母表。要求找出一个长度最短的字符串,使得它不是S的子串。

    求两个字符串的最长公共子串 

    问题.给定两个字符串S和T。要求找出它们的最长公共子串,即一个字符串X,它同时是S和T的子串。

    多个字符串的最长公共子串 
    问题.给出K个字符串S_1~S_K。要求找出它们的最长公共子串,即一个字符串X,它是所有S_i的子串。

    void ext(int c){
         int p=lst,np=lst=++cnt; mx[np]=mx[p]+1;
         l[np]=r[np]=mx[np];
         while (!son[p][c] && p) son[p][c]=np,p=fa[p];
         if (!p) fa[np]=1;
         else{
              int q=son[p][c];
              if (mx[p]+1==mx[q]) fa[np]=q;
              else{
                   int nq=++cnt; mx[nq]=mx[p]+1;
                   memcpy(son[nq],son[q],sizeof(son[q]));
                   fa[nq]=fa[q]; fa[np]=fa[q]=nq;
                   while (son[p][c]==q) son[p][c]=nq,p=fa[p];
              }
         }
    }

    广义后缀自动机,多个串放入同一个SAM,每次lst赋值为根,暂不熟练。

    https://www.cnblogs.com/candy99/p/sam.html

    后缀数组

    应用范围比后缀自动机广,但模板很容易忘记,常配合height解题。

    http://www.cnblogs.com/candy99/p/6666617.html

    http://wenku.baidu.com/link?url=Beh6Asxvtm7M2QY5kiPyKKaP87xvBrNBKW9LXOeGKm-WM4GoUM3opnHZ8z-DahF7TRaLZZ4cpUe6jfFF064XUEmAiIDF7t90CpgNfSC3_Pq

    注意:模板背熟,有时可以配合并查集使用,常用二分和按照height数组大小分组的方法。

    struct sa_array{
       int sa[N],rk[N],h[N],st[N][16],x[N],y[N],c[N]; char S[N];
       int Cmp(int a,int b,int l){ return y[a]==y[b] && y[a+l]==y[b+l]; }
       
       void build_sa(int m){
          memset(y,0,sizeof(y));
          rep(i,0,m) c[i]=0;
          rep(i,1,n) c[x[i]=S[i]-'a'+1]++;
          rep(i,1,m) c[i]+=c[i-1];
          for (int i=n; i; i--) sa[c[x[i]]--]=i;
          for (int k=1,p=0; p<n; k<<=1,m=p){
             p=0;
             rep(i,n-k+1,n) y[++p]=i;
             rep(i,1,n) if (sa[i]>k) y[++p]=sa[i]-k;
             rep(i,0,m) c[i]=0;
             rep(i,1,n) c[x[y[i]]]++;
             rep(i,1,m) c[i]+=c[i-1];
             for (int i=n; i; i--) sa[c[x[y[i]]]--]=y[i];
             rep(i,1,n) y[i]=x[i]; p=1; x[sa[1]]=1;
             rep(i,2,n) x[sa[i]]=Cmp(sa[i-1],sa[i],k) ? p : ++p;
          }
       }
       
       void get(){
           int k=0;
          rep(i,1,n) rk[sa[i]]=i;
          rep(i,1,n){
             for (int j=sa[rk[i]-1]; i+k<=n && j+k<=n && S[i+k]==S[j+k]; k++);
             h[rk[i]]=k; if (k) k--;
          }
       }
       
       void rmq(){
          rep(i,1,n) st[i][0]=h[i];
          rep(i,1,log[n])
          rep(j,1,n-(1<<i)+1) st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
       }
       
       int ask(int l,int r){
          l++; int t=log[r-l+1];
          return min(st[l][t],st[r-(1<<t)+1][t]);
       }
       
       int que(int x,int y){ return ask(min(rk[x],rk[y]),max(rk[x],rk[y]));}
       
    }SA;

    KMP,Manacher较好理解,暂略。

    扩展KMP一般可以用前述数据结构代替,还不会。

    想不到用这些数据结构维护?用哈希!

  • 相关阅读:
    iframe嵌套
    fiddler——一款莱斯的抓包工具
    工程代码规范和更新规范
    新项目新工作空间新仓库新setting文件
    quartz-job实现实时或定时发送短信任务
    quartz-job实现定时任务配置
    quartz开源作业调度框架的配置
    Confluence 6 让一个空间可以公众访问
    Confluence 6 管理和恢复空间管理权限
    Confluence 6 指派和撤销空间权限
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8178517.html
Copyright © 2011-2022 走看看