模板 - 字符串 - 字符串匹配
KMP - 一对一
计算next数组的方法是对于长度为n的匹配串,从0到n-1位依次求出前缀后缀最大匹配长度。
下面的写法是仅仅检测有没有匹配然后返回第一个匹配位置,而不是返回所有匹配位置。
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const int N=100; char str[100],ptr[100];//父串str和子串ptr int next[100]; string ans; void getnext()//获取next数组 { int i,n,k; n=strlen(ptr); memset(next,0,sizeof(next)); k=0; for(i=1;i<n;i++) { while(k>0 && ptr[k]!=ptr[i]) k=next[k]; if(ptr[k]==ptr[i]) k++; next[i+1]=k; //next表示的是匹配长度 } } int kmp(char *a,char *b)//匹配ab两串,a为父串 { int i=0,j=0; int len1=strlen(a); int len2=strlen(b); getnext(); while(i<len1&&j<len2) { if(j==0||a[i]==b[j]) { i++;j++; } else j=next[j+1];//到前一个匹配点 } if(j>=len2) return i-j; else return -1; } int main(){ while( scanf( "%s%s", str, ptr ) ) { int ans = kmp(str,ptr); if(ans>=0) printf( "%d\n", kmp( str,ptr )); else printf("Not find\n"); } return 0; }
字典树 - 一对多
其实Trie也叫做前缀树。
基本性质
1,根节点不包含字符,除根节点意外每个节点只包含一个字符。
2,从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
3,每个节点的所有子节点包含的字符串不相同。
优点:
可以最大限度地减少无谓的字符串比较,故可以用于词频统计和大量字符串排序。
跟哈希表比较:
1,最坏情况时间复杂度比hash表好
2,没有冲突,除非一个key对应多个值(除key外的其他信息)
3,自带排序功能(类似Radix Sort),先序遍历Trie可以得到排序。
可以用来得到字符串最长公共前缀(转成LCA问题/最近公共祖先问题,用Tarjan算法,dfs某个节点的子树,子树之间的相互的最近公共祖先就是这个节点)
#include<queue> #include<set> #include<cstdio> #include <iostream> #include<algorithm> #include<cstring> #include<cmath> using namespace std; /* trie字典树 */ struct tnode{ int sum;//用来判断是否是终点的 tnode* next[26]; tnode(){ for(int i =0;i<26;i++) next[i]=NULL; sum=0; } }; tnode *root; tnode* newnode(){ tnode *p = new tnode; for(int i =0;i<26;i++) p->next[i]=NULL; p->sum=0; return p; } //插入函数 void Insert(char *s) { tnode *p = root; for(int i = 0 ; s[i] ; i++) { int x = s[i] - 'a'; if(p->next[x]==NULL) { tnode *nn=newnode(); for(int j=0;j<26;j++) nn->next[j] = NULL; nn->sum = 0; p->next[x]=nn; } p = p->next[x]; } p->sum++;//这个单词终止啦 } //匹配函数 bool Compare(char *ch) { tnode *p = root; int len = strlen(ch); for(int i = 0; i < len; i++) { int x = ch[i] - 'a'; p = p->next[x]; if(p==NULL) return false; if(i==len-1 && p->sum>0 ){ return true; } } return false; } void DELETE(tnode * &top){ if(top==NULL) return; for(int i =0;i<26;i++) DELETE(top->next[i]); delete top; } int main() { int n,m; cin>>n; char s[20]; root = newnode(); for(int i =0;i<n;i++){ scanf("%s",s); Insert(s); } cin>>m; for(int i =0;i<m;i++){ scanf("%s",s); if(Compare(s)) cout<<"YES"<<endl; else cout<<"NO"<<endl; } DELETE(root);//看见指针就要想到释放,然而这东西会花时间,所以网上很多人写ACM题就不delete了,我很看不惯这一点。 return 0; }
AC自动机
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> using namespace std; struct Trie { int next[210*500][128],fail[210*500],end[210*500]; int root,L; int newnode() { for(int i = 0; i < 128; i++) next[L][i] = -1; end[L++] = -1; return L-1; } void init() { L = 0; root = newnode(); } void insert(char s[],int id) { int len = strlen(s); int now = root; for(int i = 0; i < len; i++) { if(next[now][s[i]] == -1) next[now][s[i]] = newnode(); now=next[now][s[i]]; } end[now]=id; } void build() { queue<int>Q; fail[root] = root; for(int i = 0; i < 128; i++){ if(next[root][i] == -1) next[root][i] = root; else { fail[next[root][i]] = root; Q.push(next[root][i]); } } while(!Q.empty()) { int now = Q.front(); Q.pop(); for(int i = 0; i < 128; i++){ if(next[now][i] == -1) next[now][i] = next[fail[now]][i]; else { fail[next[now][i]] = next[fail[now]][i]; Q.push(next[now][i]); } } } } bool used[510]; bool query(char buf[],int n,int id) { int len = strlen(buf); int now = root; memset(used,false,sizeof(used)); bool flag = false; for(int i = 0; i < len; i++) { now = next[now][buf[i]]; int temp = now; while(temp != root) { if(end[temp] != -1) { used[end[temp]] = true; flag = true; } temp = fail[temp]; } } if(!flag) return false; printf("web %d:",id); for(int i = 1; i <= n; i++) if(used[i]) printf(" %d",i); printf("\n"); return true; } }; char buf[10010]; Trie ac; int main() { int n,m; while(~scanf("%d",&n)) { ac.init(); for(int i = 1; i <= n; i++) { scanf("%s",buf); ac.insert(buf,i); } ac.build(); int ans = 0; scanf("%d",&m); for(int i = 1; i <= m; i++) { scanf("%s",buf); if(ac.query(buf,n,i)) ans++; } printf("total: %d\n",ans); } return 0; }