• [Usaco2015 Feb] [Bzoj3942] Censoring


    1.KMP版Censoring

     题干:

      给出两个字符串 S 和 T,每次从前往后找到 S 的一个子串 A=T 并将其删除,空缺位依次向前补齐,重复上述操作多次,直到 S 串中不含 T 串。输出最终的 S 串。

     题解:

      这是一道KMP入门题,像题目描述一样,KMP最主要的应用就是进行单模式匹配(给出一个母串与一个目标串进行匹配)。其实KMP的核心只有一个Next[]数组。Next[]表示的是目标串的一个节点,这个目标串的节点及以前的部分 与 母串的某一部分一一对应,而那个母串的某一部分的最后一位就是Next[]的下标。因为本题还涉及到不断删除与拼接字符串,一开始我就想先删一遍,然后接上字符串后再删一遍,如此重复。最后是又WA又TLE。。。其实应该将或匹配或不匹配的节点压入栈中,当匹配的数量正好为目标串的长度时,弹掉栈的头几个(目标串长度个)并拿出栈顶继续匹配(节省了重匹配的时间,也方便了最后的弹栈输出)。Get it!

     Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #define $ 1111111
     4 using namespace std;
     5 char a[$],s[$];
     6 int Next[$],judge[$],m,n,k,t,lengths,lengtha,q[$],up;
     7 //Next[i]定义的是在1——i中,最长的既是前缀又是后缀的长度 
     8 signed main(){
     9     scanf("%s%s",s+1,a+1);
    10     lengths=strlen(s+1); lengtha=strlen(a+1);
    11     Next[1]=0;
    12     for(register int i=1,nex=0;i<=lengtha;++i){//
    13         while(nex!=0&&a[i]!=a[nex]) nex=Next[nex];
    14         Next[i+1]=++nex;
    15     }//预处理出小串的 Next,失配时返回可匹配的最长串 
    16     for(register int i=1;i<=lengtha;++i) printf("%d ",Next[i]); 
    17     for(register int i=1,nex=1;i<=lengths;++i){
    18         while(nex!=0&&s[i]!=a[nex]) nex=Next[nex];
    19         //如果失配,跳到上一次出现已匹配到串的位置 
    20         nex++;//跳到下一位 
    21         q[++up]=i;//压栈 
    22         judge[up]=nex;
    23         if(nex==lengtha+1) up-=lengtha,nex=judge[up]; 
    24         //如果匹配成功(nex已经++过了,所以要等于 lengtha+1)
    25         //跳栈并返回上一次匹配位置 
    26     }
    27     for(register int i=1;i<=up;++i) printf("%c",s[q[i]]);
    28     return 0;
    29 }
    Code

    2.AC自动机版Ceosoring

    题干:

      FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过10^5的字符串S,他有一个包含 n 个单词的列表,列表里的n个单词记为t1----tn,他希望从S中删除这些单词。FJ每次在 S 中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致 S 中出现另一个列表中的单词,FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的。请帮助 FJ 完成这些操作并输出最后的 S。

    题解:

      AC自动机相对于KMP只是将单模式匹配转变为多模式匹配(就是目标串多了),由Next[]变为fail[]而已。同样是不断压栈,弹栈,最后倒序输出。在trie树(图)上,可以理解为母串挂在这棵树上跑。具体AC自动机内容点这里

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>// next 为保留字!!!!!!!!!!! 
     4 #define $ 111111
     5 using namespace std;
     6 char s[$],ss[$];
     7 int m,n,k,tot,length;
     8 struct trie{    int size; trie *fail,*son[27];    }*sta[$*1000];
     9 queue<trie*> q;
    10 inline trie *newnode(){
    11     trie *p=new trie;
    12     p->size=0; p->fail=NULL;
    13     for(register int i=0;i<=25;++i) p->son[i]=NULL;
    14     return p;
    15 }
    16 inline void insert(trie *root){
    17     trie *p=root;
    18     length=strlen(ss+1);
    19     for(register int i=1;i<=length;++i){
    20         int x=ss[i]-'a';
    21         if(p->son[x]==NULL) p->son[x]=newnode();
    22         p=p->son[x];
    23     }
    24     p->size=length;//维护的是子串长度 
    25 }
    26 inline void build(trie *root){// trie图 
    27 //    q.push(root);    //只需直接维护儿子,不可将 root 入队 
    28     for(register int i=0;i<=25;++i){
    29         if(root->son[i]==NULL) root->son[i]=root;//周围一堆儿子 
    30         else root->son[i]->fail=root,q.push(root->son[i]);
    31     }
    32     while(q.size()){
    33         trie *p=q.front();  q.pop();
    34         for(register int i=0;i<=25;++i){
    35             if(p->son[i]!=NULL){
    36                 p->son[i]->fail=p->fail->son[i];
    37                 q.push(p->son[i]);
    38             }
    39             else p->son[i]=p->fail->son[i];//将 trie 树补全 
    40         }
    41     }
    42 }
    43 signed main(){
    44     trie *root=newnode();
    45     scanf("%s%d",s+1,&n);
    46     for(register int i=1;i<=n;++i) scanf("%s",ss+1),insert(root);
    47     build(root);  
    48     sta[0]=root;//当在过程中栈被跳空,需有一个 root 缓冲 
    49     length=strlen(s+1);//注意:strlen 时间复杂度 O(n) 不是O(1) 
    50     for(register int i=1;i<=length;++i){
    51         int x=s[i]-'a';
    52         root=root->son[x];//在trie树上跑,找相应单词
    53         //若没有,就为 root(NULL) 
    54         sta[++tot]=root; ss[tot]=s[i];//标记并压栈 
    55         if(root->size) tot-=root->size,root=sta[tot];
    56         //如果匹配成功,跳回此单词出现位置之前,但 i 不变 
    57         //将 root 置为以前状态 
    58     }
    59     for(register int i=1;i<=tot;++i) printf("%c",ss[i]);
    60 }
    Code

    3.Hash版Censoring

    题干:(同第二题)

      FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过10^5的字符串S,他有一个包含 n 个单词的列表,列表里的n个单词记为t1----tn,他希望从S中删除这些单词。FJ每次在 S 中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致 S 中出现另一个列表中的单词,FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的。请帮助 FJ 完成这些操作并输出最后的 S。

    题解:

      Hash其实就是将一个字符串压成一个数,在比对时像二进制左移右移一样调整位置(在Hash中往往是一个适当大小的质数进制,防止两段字符串不相等但Hash值相等的事故)。

    Hash值因为可以减小O(len)的时间复杂度而在实际应用(骗分)中大受欢迎。。。

    Code:

     1 #include<cstdio>
     2 #include<cstring>
     3 #define $ 200010
     4 #define ull unsigned long long
     5 #define zzyy 13131
     6 using namespace std;
     7 int lens,n,m,len[$],istack[$*100],top;
     8 ull hashss[$],prime[$],hstack[$*100];
     9 char s[$],ss[$];
    10 signed main(){
    11     prime[0]++;
    12     for(register int i=1;i<=100001;++i) prime[i]=prime[i-1]*zzyy;//预处理 
    13     scanf("%s%d",s+1,&n);  lens=strlen(s+1);
    14     for(register int i=1;i<=n;++i){
    15         scanf("%s",ss+1);
    16         len[i]=strlen(ss+1);
    17         for(register int j=1;j<=len[i];++j){
    18             hashss[i]=hashss[i]*zzyy+ss[j]-'a'+1;
    19             //更新每一个串的hash值,压成一个点与主串比较 
    20         }
    21     }
    22     for(register int i=1;i<=lens;++i){
    23         istack[++top]=i;
    24         hstack[top]=hstack[top-1]*zzyy+s[i]-'a'+1;//正常每位压 hash 
    25         for(register int j=1;j<=n;++j){
    26             if(top-len[j]<0) continue;
    27             if(hstack[top]-hstack[top-len[j]]*prime[len[j]]==hashss[j]){
    28                 top-=len[j]; break;//比较 
    29             }
    30         }
    31     }
    32     for(register int i=1;i<=top;++i) putchar(s[istack[i]]);
    33 }
    Code
    越努力 越幸运
  • 相关阅读:
    Android Widget桌面组件创建
    Android 音乐播放器。
    android 创建实时文件夹
    Android 中访问网页 webView
    Android 添加快捷方式
    Android闹钟设置
    Android中webView与javascript交互
    Android 通信的桥梁 Intent
    Android 中Notification和NotificationManager的使用
    Android中播放视频
  • 原文地址:https://www.cnblogs.com/OI-zzyy/p/11110069.html
Copyright © 2020-2023  润新知