题目大意
给定一个文本串,接下来有n个模式串,每次查询模式串出现的次数,查询分两种,可重叠和不可重叠
题解
第一次是把AC自动机构造好,跑n次,统计出每个模式串出现的次数,交上去果断TLE。。。后来想想其实只要跑一次文本串即可。。。
这个题目主要的问题是要解决有可重叠和不可重叠两种情况,用一个数组记录下模式串上一次出现的位置就可以判断是否重叠了
代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <queue> #include <vector> #include <cstring> using namespace std; #define MAXN 100005 const int maxnode=600005; const int sigma_size=26; char T[MAXN],s[7]; bool flag[MAXN]; int pos[MAXN]; struct Triegragh { int ch[maxnode][sigma_size]; int fail[maxnode]; int deep[maxnode]; int cnt[maxnode][2]; int end[maxnode]; int last[maxnode]; int val[maxnode]; int sz; void init() { sz=1; memset(ch[0],0,sizeof(ch[0])); memset(cnt,0,sizeof(cnt)); memset(end,-1,sizeof(end)); memset(val,0,sizeof(val)); } int idx(char c){return c-'a';} int insert(char *s) { int u=0,n=strlen(s); for(int i=0;i<n;i++) { int c=idx(s[i]); if(!ch[u][c]) { memset(ch[sz],0,sizeof(ch[sz])); val[sz]=0; deep[sz]=i+1; ch[u][c]=sz++; } u=ch[u][c]; } val[u]++; return u; } void getfail() { queue<int>q; fail[0]=0; for(int c=0;c<sigma_size;c++) { int u=ch[0][c]; if(u){fail[u]=0;q.push(u);last[u]=0;} } while(!q.empty()) { int r=q.front();q.pop(); for(int c=0;c<sigma_size;c++) { int u=ch[r][c]; if(!u){ch[r][c]=ch[fail[r]][c];continue;} q.push(u); fail[u]=ch[fail[r]][c]; last[u]=val[fail[u]]?fail[u]:last[fail[u]]; } } } void count(int j,int i) { if(j) { cnt[j][0]++; if(i-end[j]>=deep[j]) { cnt[j][1]++; end[j]=i; } count(last[j],i); } } void find(char *T) { int j=0,n=strlen(T); for(int i=0;i<n;i++) { int c=idx(T[i]); j=ch[j][c]; if(val[j]) count(j,i); else if(last[j]) count(last[j],i); } } }; Triegragh ac; int main() { int kase=0; while(scanf("%s",T)!=EOF) { printf("Case %d ",++kase); int n; scanf("%d",&n); ac.init(); for(int i=1;i<=n;i++) { scanf("%d%s",&flag[i],s); pos[i]=ac.insert(s); } ac.getfail(); ac.find(T); for(int i=1;i<=n;i++) printf("%d ",ac.cnt[pos[i]][flag[i]]); printf(" "); } return 0; }