• hdu1686 Oulipo KMP/AC自动机


    The French author Georges Perec (1936–1982) once wrote a book, La disparition, without the letter 'e'. He was a member of the Oulipo group. A quote from the book:

    Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…

    Perec would probably have scored high (or rather, low) in the following contest. People are asked to write a perhaps even meaningful text on some subject with as few occurrences of a given “word” as possible. Our task is to provide the jury with a program that counts these occurrences, in order to obtain a ranking of the competitors. These competitors often write very long texts with nonsense meaning; a sequence of 500,000 consecutive 'T's is not unusual. And they never use spaces.

    So we want to quickly find out how often a word, i.e., a given string, occurs in a text. More formally: given the alphabet {'A', 'B', 'C', …, 'Z'} and two finite strings over that alphabet, a word W and a text T, count the number of occurrences of W in T. All the consecutive characters of W must exactly match consecutive characters of T. Occurrences may overlap.

    题意:给出了两个字符串,问第一个串在第二个中可以重复地出现过多少次。

    我一开始是用AC自动机做了一次,建了模式串的AC自动机后,待匹配串每次走到词尾节点ans++。

    后来又用KMP做了一次,先模式串自匹配,再与待匹配串匹配的时候记录ans

     1 // 有nxt数组版本。。该程序不能判别相同模式串,因此若模式串重复,答案会将相同模式串当做不同的处理,因此若需要可以用map去重或修改insert
     2 #include<stdio.h>
     3 #include<string.h>
     4 #include<queue>
     5 using namespace std;
     6 const int maxm=5000006;        //maxm是总结点数:约为字母数+++
     7 
     8 char s[1000005],word[10005];
     9 int nxt[maxm][26],tail[maxm],f[maxm],size;        //nxt是结点指向不同字母的结点下标,tail是表示该结点为几个单词的词尾(可能需要计算重复的模式串情况),f是当不匹配时转跳到的结点下标,size是结点数
    10 int last[maxm];    //last指针是指向上一个是单词结尾的结点,由于是由失配指针拓展得到的,因此所指向的单词都是该结点表示的单词的后缀单词,但由于可能卡空间,所以虽然可以在时间上优化,但是有时并不使用
    11 
    12 int newnode(){        //初始化整个trie或建立新的结点时,首先初始化当前结点所指向的26个字母的结点为0,表示暂时还没有指向的字母,然后暂定该结点不是单词尾结点,暂无失配时转跳位置(即转跳到根节点),返回结点标号
    13     memset(nxt[size],0,sizeof(nxt[size]));
    14     f[size]=tail[size]=0;
    15     return size++;
    16 }
    17 
    18 void insert(char s[]){    //构造trie,p为当前结点的上一个结点标号,初始为0;x即为当前结点(上个结点标号指向当前字母的结点)标号,若此结点还未出现过,那么就建立这个结点;然后更新p为当前结点标号以便后续操作
    19     int i,p=0;
    20     for(i=0;s[i];i++){
    21         int &x=nxt[p][s[i]-'A'];
    22         p=x?x:x=newnode();
    23     }
    24     tail[p]++;    //此时仅将s串记录,即将s串结尾的结点加1,若无相同模式串,则此操作只会使所有串尾结点的tail值由0变为1,但有相同模式串,则会重复记录,需要去重可以用map或用tail[p]=1;语句来完成
    25 }
    26 
    27 void makenxt(){    //利用bfs来构造失配指针
    28     int i;
    29     queue<int>q;
    30     f[0]=0;
    31     for(i=0;i<26;i++){    //首先将0结点(根节点)连接的字母结点加入队列,并定失配指针和last指针都指向0结点
    32         int v=nxt[0][i];
    33         if(v){
    34             f[v]=last[v]=0;
    35             q.push(v);
    36         }
    37     }
    38     while(!q.empty()){
    39         int u=q.front();
    40         q.pop();
    41         for(i=0;i<26;i++){
    42             int v=nxt[u][i];
    43             if(!v)nxt[u][i]=nxt[f[u]][i];    //当u结点没有i对应字母,则视为失配,将其指向失配后转跳到的结点所指向的i对应字母
    44             else{
    45                 q.push(v);    //u结点存在指向i的结点,则将所指向的结点下标加入队列
    46                 f[v]=nxt[f[u]][i];    //设置这个结点的失配指针指向上个结点失配后的指向字母i的结点,由于bfs一定会从字典树浅层到深层,即从字符串短到长,而失配转跳后表示的字符串长度严格减少,所以只需要指向一次即可
    47                 last[v]=tail[f[v]]?f[v]:last[f[v]];    //若失配指针指向的结点是单词结尾,那么当前结点失配后就可以直接指向失配结点,即失配路径上的上一个单词结点,若失配结点不是单词结尾,就指向失配结点的last
    48             }
    49         }
    50     }
    51 }
    52 
    53 int query(char s[]){    //查询s串中模式串出现了多少种/次
    54     int ans=0,v=0;
    55     for(int i=0;s[i];i++){
    56         while(v&&!nxt[v][s[i]-'A'])v=f[v];
    57         v=nxt[v][s[i]-'A'];
    58         int tmp=v;
    59         while(tmp){
    60             ans+=tail[tmp];
    61             tmp=last[tmp];
    62         }
    63     }
    64     return ans;
    65 }
    66 
    67 int main(){
    68     int T;
    69     scanf("%d",&T);
    70     while(T--){
    71         size=0,newnode();
    72         scanf("%s",word);
    73         insert(word);
    74         makenxt();
    75         scanf("%s",s);
    76         printf("%d
    ",query(s));
    77     }
    78     return 0;
    79 }
    AC自动机
     1 #include<stdio.h>
     2 #include<string.h>
     3 
     4 char s[1000005],t[10005];    //s为待匹配串,t为模板串
     5 int p[10005];    //自匹配数组
     6 
     7 int main(){
     8     int T;
     9     scanf("%d",&T);
    10     while(T--){
    11         scanf("%s%s",t,s);
    12         int n=strlen(s);
    13         int m=strlen(t);
    14         int i,j,ans=0;
    15         p[0]=p[1]=0;
    16         for(i=1;i<m;i++){
    17             j=p[i];
    18             while(j&&t[i]!=t[j])j=p[j];
    19             p[i+1]=t[i]==t[j]?j+1:0;
    20         }
    21         j=0;
    22         for(i=0;i<n;i++){
    23             while(j&&s[i]!=t[j])j=p[j];
    24             if(s[i]==t[j])j++;
    25             if(j==m)ans++;
    26         }
    27         printf("%d
    ",ans);
    28     }
    29     return 0;
    30 }
    KMP
  • 相关阅读:
    PHP多条件模糊查询
    纯干货!一款APP从设计稿到切图过程全方位揭秘(转)
    0532. K-diff Pairs in an Array (M)
    0933. Number of Recent Calls (E)
    0139. Word Break (M)
    0713. Subarray Product Less Than K (M)
    0399. Evaluate Division (M)
    0495. Teemo Attacking (M)
    0179. Largest Number (M)
    0389. Find the Difference (E)
  • 原文地址:https://www.cnblogs.com/cenariusxz/p/6592241.html
Copyright © 2020-2023  润新知