字典树,AC自动机
字典树
概述:给定n个字符串,进行m次询问,每次询问给一个字符串t,问在n个字符串里有几个是字符串t的前缀.
思路:字典树,每个点记一下,以这个点结尾的字符串有几个,查询的时候,一边走,一遍加.
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=1e6+10; const int maxxn=1e6+10; struct node{ int end; int a[26]; }trie[maxxn]; int tot=1; void insert(char *str){ int len=strlen(str),p=1; for(int k=0;k<len;k++){ int ch=str[k]-'a'; if(trie[p].a[ch]==0) trie[p].a[ch]=++tot; p=trie[p].a[ch]; } trie[p].end++; } int search(char *str){ int len=strlen(str),p=1,res=0; for(int k=0;k<len;k++){ p=trie[p].a[str[k]-'a']; res+=trie[p].end; if(p==0) break; } return res; } int main(){ int n,m; scanf("%d%d",&n,&m); char s[maxn]; for(int i=0;i<n;i++){ scanf("%s",s); insert(s); } for(int i=0;i<m;i++){ scanf("%s",s); printf("%d\n",search(s)); } return 0; }
AC自动机
AC自动机能解决单个母串,多个子串的问题,例如某一个子串出现了多少次,一共出现了几个子串等等.
简单来说为三步
1.建立子串的字典树
2建立fail指针,从而添加失败路径
3.跑母串
https://blog.csdn.net/bestsort/article/details/82947639?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-1.control
有N个由小写字母组成的模式串以及一个文本串T。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串TT中出现的次数最多。
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> using namespace std; const int maxn=1000010; int trie[maxn][26]; int fail[maxn]; int sum[maxn]; char str1[200][100]; char str2[maxn]; int tot=0,eend[maxn],ans[200]; void init(void){ memset(trie,0,sizeof(trie)); memset(fail,0,sizeof(fail)); memset(sum,0,sizeof(sum)); memset(eend,0,sizeof(eend)); memset(ans,0,sizeof(ans)); tot=0; } //建立字典树 void _insert(int a){ int p=0,k,len=strlen(str1[a]); for(int i=0;i<len;i++){ k=str1[a][i]-'a'; if(!trie[p][k]) trie[p][k]=++tot; p=trie[p][k]; } sum[p]=1; eend[p]=a; } //获得每个点的fail值 void getfail(void){ queue<int>q; for(int i=0;i<26;i++){ if(trie[0][i]){ fail[trie[0][i]]=0; q.push(trie[0][i]); } } while(q.size()){ int p; int now=q.front(); q.pop(); for(int i=0;i<26;i++){ p=trie[now][i]; if(p) fail[p]=trie[fail[now]][i],q.push(p); else trie[now][i]=trie[fail[now]][i]; //如果这个点不存在就直接将这个点的编号设为其上一个点fail指针指向的点的下一个 } } } //查找 void _search(){ int p=0,k,len=strlen(str2); for(int i=0;i<len;i++){ k=str2[i]-'a'; p=trie[p][k]; for(int j=p;j;j=fail[j]) ans[eend[j]]+=sum[j]; } return ; } int main(void){ int n; while(scanf("%d",&n),n){ init(); for(int i=1;i<=n;i++){ scanf("%s",str1[i]); _insert(i); } getfail(); scanf("%s",str2); _search(); int maxx=-1; for(int i=1;i<=n;i++) maxx=max(maxx,ans[i]); printf("%d\n",maxx); for(int i=1;i<=n;i++){ if(ans[i]==maxx) printf("%s\n",str1[i]); } } return 0; }
fail树的写法下次再写