CSU2004:Finding words(含指定不相交前后缀的模式串计数)
题:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=2004
题意:给定n个模式串,m个询问,每个询问是“前缀+‘*’+后缀 ”的组合的串S,输出n个模式串中有几个和S是相同的,‘*’可以是0和或更多的字符组成
4 and abandon append island 3 ab* *nd a*nd
output
1 3 2
分析:一般是用给定的模式串来减trie图,但这题显然比较困难,解不出,那么就考虑反过来,我们拿询问的字符串进行构建;
这里要做一下处理:以“后缀+’z+1‘+前缀”地插入trie图,z+1用来代替特殊字符;
因为题目要求的是类似断开的询问串,而我们可以想象一下询问串的首位相连的话,同时将n个模式串首尾链接的话,前缀和后缀就连在一起了且具有唯一性,所以只要我们只要以模式串来跑trie图就行了;
n个模式串的首尾相连根据trie的处理来,就是直接s+’z+1‘+s即可;
至于为啥要用’z+1‘来+在首尾相连的地方,因为如果不加的话,可能这样处理会创造出新的原本不存在的序列出来;
注意,在跑ac自动机的时候要判断长度是否满足条件,因为我们这里相当于把n个模式串扩大了俩倍+1,而查询串的长度不变。
#include<iostream> #include<cstdio> #include<queue> #include<algorithm> #include<cstring> using namespace std; const int M=1e6+5; const int N=1e5+5; const int maxn=27; int ans[N]; struct ac{ int trie[M][maxn],end[M],fail[M],Len[M]; int root,tot; int newnode(){ for(int i=0;i<maxn;i++){ trie[tot][i]=-1; } end[tot++]=0; return tot-1; } void init(){ tot=0; root=newnode(); } void insert(string buf,int id){ int len=buf.size(); int now=root; for(int i=0;i<len;i++){ if(trie[now][buf[i]-'a']==-1) trie[now][buf[i]-'a']=newnode(); now=trie[now][buf[i]-'a']; } end[now]=id; Len[now]=len-1; } void getfail(){ queue<int>que; while(!que.empty()){ que.pop(); } fail[root]=root; for(int i=0;i<maxn;i++){ if(trie[root][i]==-1) trie[root][i]=root; else{ fail[trie[root][i]]=root; que.push(trie[root][i]); } } while(!que.empty()){ int now=que.front(); que.pop(); for(int i=0;i<maxn;i++){ if(trie[now][i]!=-1){ fail[trie[now][i]]=trie[fail[now]][i]; que.push(trie[now][i]); } else trie[now][i]=trie[fail[now]][i]; } } } void query(string buf){ int len=buf.size(); int now=root; int flag=(len-1)/2; for(int i=0;i<len;i++){ now=trie[now][buf[i]-'a']; int tmp=now; while(tmp!=root){ if(Len[tmp]&&end[tmp]!=0&&flag>=Len[tmp]) ans[end[tmp]]++; tmp=fail[tmp]; } } } }AC; char sign='z'+1; string s[M]; char buf[33]; int main(){ AC.init(); int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",buf); s[i]=buf; s[i]=s[i]+sign+s[i]; } int m; scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%s",buf); string s2=buf; if(s2=="*"){ ans[i]=n; continue; } int j; for(j=0;j<s2.size();j++){ if(s2[j]=='*') break; } s2=s2.substr(j+1,s2.size()-j-1)+sign+s2.substr(0,j); AC.insert(s2,i); } AC.getfail(); //cout<<"!!"<<endl; for(int i=1;i<=n;i++){ AC.query(s[i]); } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }