• 字符串相关算法


    KMP

    问题:给定文本串S和模式串P,查找P在S中的位置

    现在 S 匹配到了 i , P 匹配到了 j . 若 s [ i + 1 ] ! = p [ j + 1 ] , 需要重新匹配。

    如果是暴力算法,需要重头匹配,真的有必要吗?

    我们已知 i 匹配到了 j ,如果 有一个 k ,p [ 1 ~ k ] == p [ ( j - k + 1 )~j ],就可以跳到 k 进行匹配,降低复杂度。

    (这里应该有张图)

    记一个 nxt 数组, nxt [ i ] 表示 p [ 1~i ] 的 最长公共前后缀, 求的方法和上面类似。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 using namespace std;
     6 const int N = 1000005;
     7 char p[N],s[N];
     8 int nxt[N];
     9 int ls,lp;
    10 inline void pre(){
    11     nxt[1]=0;
    12     int j=0;
    13     for(int i=2;i<=lp;i++){
    14         while(p[i]!=p[j+1]&&j)j=nxt[j];
    15         if(p[i]==p[j+1])++j;
    16         nxt[i]=j;
    17     }
    18 }
    19 inline void solve(){
    20     int j=0;
    21     for(int i=1;i<=ls;i++){
    22         while(s[i]!=p[j+1]&&j)j=nxt[j];
    23         if(s[i]==p[j+1])++j;
    24         if(j==lp){
    25             cout<<i-lp+1<<endl;
    26             j=nxt[j];
    27         }
    28     }
    29 }
    30 int main(){
    31     scanf("%s%s",s+1,p+1);
    32     ls=strlen(s+1);lp=strlen(p+1);
    33     pre();
    34     solve();
    35     for(int i=1;i<=lp;i++)cout<<nxt[i]<<" ";
    36 }

    复杂度O(N)

    Trie

    建树,边上是字母标号,沿着树边向下走,没有边了就新建。

    Manacher

    问题:对于一个字符串T,求出其最长回文子串。

    回文串有奇回文串和偶回文串,为了保证回文串的回文中心都是字符,需要在每个字符后加一个其他字符(比如 ' $ ');

    假设求出了以 i 为中心的最长回文半径 R [ i ] ,对于在 [ i + 1, i + R [ i ] ] 内的点 q ,必定在 [ i - R [ i ] +1 ,i - 1 ] 有一个对应点 p ,且 R [ p ] 已经求出。

    如果 R [ p ] 对称过来没有超出  ( i + R [ i ] ),显然可以。

    超出来了就暴力嘛。

    (这里应该有张图)

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 using namespace std;
     6 const int N = 11000005;
     7 char s[N*2],c[N];
     8 int len,maxlen=0,R[N*2];
     9 int main(){
    10     scanf("%s",c+1);
    11     len=strlen(c+1);
    12     for(int i=1;i<=len*2+1;i++){
    13         if(i%2)s[i]='$';
    14         else s[i]=c[i/2];
    15     }
    16     s[0]='%';
    17     int mx=0,id;
    18     for(int i=1;i<=len*2+1;i++){
    19         if(i<mx)R[i]=min(R[2*id-i],mx-i);
    20         else R[i]=1;
    21         while(s[i-R[i]]==s[i+R[i]])R[i]++;
    22         if(mx<i+R[i]){
    23             mx=i+R[i];
    24             id=i;
    25         }
    26         maxlen=max(maxlen,R[i]-1);
    27     }
    28     printf("%d",maxlen);
    29 }

    AC自动机(!=自动AC机)

    问题:给m个模板串,用一个母串进行匹配。判断那些模板串在母串中出现过,出现了几次。

    在trie上跑个KMP,对于每个节点求出失配指针,相当于nxt[]。

     1 #include<iostream>
     2 #include<string>
     3 #include<cstdio>
     4 #include<cstring>
     5 #include<queue>
     6 using namespace std;
     7 const int N=1000005;
     8 
     9 char ch[N<<1];//文本串 
    10 struct dot{
    11     int cnt,f,a[30];//f->fail 
    12 }t[N];
    13 int ans=0,tot=0;
    14 queue<int> q;
    15 char s[N];
    16 int n;
    17 
    18 void build(){
    19     int len=strlen(s);
    20     int j=0,now=0;
    21     for(int i=0;i<len;i++){
    22         j=s[i]-'a';
    23         if(!t[now].a[j])t[now].a[j]=++tot;
    24         now=t[now].a[j];
    25     }
    26     t[now].cnt++;
    27 }
    28 
    29 inline void build_fail(){
    30     int now=0;
    31     while(q.size())q.pop();
    32     for(int i=0;i<26;i++){
    33         int j=t[now].a[i];
    34         if(j){
    35             q.push(j);
    36             t[j].f=now;//将第一层的f指向根 
    37         }
    38     }
    39     while(!q.empty()){
    40         now=q.front();q.pop();
    41         for(int i=0;i<26;i++){
    42             int j=t[now].a[i];
    43             if(!j){
    44                 t[now].a[i]=t[t[now].f].a[i];
    45                 //如果没有这个点,直接指向f的对应节点 
    46                 continue;
    47             }
    48             t[j].f=t[t[now].f].a[i];
    49             //儿子的fail等于fail的儿子,对应KMP:nxt[i]=++j; 
    50             q.push(j);
    51         }
    52     }
    53 }
    54 
    55 inline void solve(){
    56     int now=0,len=strlen(ch);
    57     for(int i=0;i<len;i++){
    58         int x=ch[i]-'a';
    59         int j=t[now].a[x];
    60         while(j&&t[j].cnt!=-1){//存在节点并未被统计 
    61             ans+=t[j].cnt;
    62             t[j].cnt=-1;
    63             j=t[j].f;
    64         }
    65         now=t[now].a[x];
    66     }
    67 }
    68 
    69 
    70 int main(){
    71     scanf("%d",&n);
    72     while(n--){
    73         scanf("%s",s);
    74         build();
    75     }
    76     build_fail();
    77     scanf("%s",ch);
    78     solve();
    79     printf("%d",ans);
    80 }

    (现在还在海亮,该补的图回去也不会补的)

  • 相关阅读:
    RabbitMQ安装(发生系统错误5。拒绝访问。发生系统错误1067。进程意外终止。)
    SQLServer执行脚本提示“系统找不到指定的文件”或“内存资源不足”
    TypeScript@HelloWorld!
    超详细Node安装教程
    进制转换
    菜鸟成长记
    ASP.NET Core中使用MialKit实现邮件发送
    VS未能正确加载 ”Microsoft.VisualStudio.Editor.Implementation.EditorPackate“包错误解决方法
    C#Winfrom Listview数据导入Excel
    安装研发服务器
  • 原文地址:https://www.cnblogs.com/chiyo/p/11160656.html
Copyright © 2020-2023  润新知