AC自动机(Trie 板子)&后缀数组模板(解决原串与子串问题)

大自然的帮运工

使用板子注意事项:

注意:
1.串是否全是小写字母
2.根节点为1
3.节点数不超过maxn
4.多组数据注意 清空
5.复杂度嘛。。。。。建树o(n*len)

前置知识:Trie树

在这里插入图片描述

需求:
给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过。
输入n个模式串, 一个原串
输出结果: 原串中含有模式串的个数。

in:
6
abcd
cd
cd
cdac
aab 
abcdac
out:
5
#include<bits/stdc++.h>
#define maxn 1000001
using namespace std;
struct kkk{
	int son[26];  //一般是小写字母 所以26个 
	int flag; ///相同模式串出现次数 或者是标记 
    int fail;  /// 失配指针
    
}trie[maxn];   // maxn 树节点数 
int n, cnt;   // n个模式串  初始根编号 1 
string s;     // 子串 
//建字典树 
void insert(string s){
	int u = 1;
	int len = s.length();
	for (int i = 0; i < len;i++){
		int v = s[i] - 'a';
		if(!trie[u].son[v]){
			trie[u].son[v] = ++cnt;
		}
		u = trie[u].son[v];
	}
	trie[u].flag++;
}
// 设置失配指针   
// fail  当前字符串最长的后缀字符串 在Trie上的可以找到的编号 
void getFail(){
	
	for (int i = 0; i < 26;i++){
		trie[0].son[i] = 1;   
	}
	queue<int> q;
	q.push(1);
	// 失配 
	trie[1].fail = 0;
	while(!q.empty()){
		int fa = q.front();
		//cout<<fa<<endl;
		q.pop();
		int fafail = trie[fa].fail;
		for (int i = 0; i < 26;i++){
			int v = trie[fa].son[i];
			// 下面trie[v].fail = trie[fafail].son[i];
			// 有点dp的味道  
            if(!v){
            	//fa没有该v节点 所以用他的最长后缀字符串
				// 的儿子节点来代替他。 
				trie[fa].son[i] = trie[fafail].son[i];
				continue;
			}
			
			trie[v].fail = trie[fafail].son[i];
			q.push(v);
		}
	}
}
// 原串与模式串的 关系 
int query(string s){
	int u = 1, ans = 0;
	int len = s.length();
	// 这个循环也很好理解
	// 其实就是两个位置 i是串的右边,现在找该串的左边的位置j
	//  要求j到i是该模式串,那要怎么做呢,用前面的trie树和失配指针
	// 当然对于每个i有很多个j嘛, 模式串又不止一个是吧
	//  
	for (int i = 0; i < len;i++){
		int v = s[i] - 'a';
		int k = trie[u].son[v];
		 // while 模式串不止一个 所以有种关系是可以找出j
		 //  关系即是 k=trie[u].fail;  不断迭代 直至没得了 
		while(k>1 and trie[k].flag!=-1){
			ans += trie[k].flag;
			trie[k].flag = -1;
			k = trie[k].fail;
		}
		u = trie[u].son[v];
	}
	return ans;
}
int main(){
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
	cnt = 1; 
	// Trie树  根节点是 1 ,什么都没有
	// 类型头指针 
	cin >> n;
	for (int i = 1; i <= n;i++){
		cin >> s;
		insert(s);
	}
	getFail();
	cin >> s;
	cout << query(s);
	return 0;
}

Two(板子)

in:
2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0
out:
4
aba
2
alpha
haha

需求:
1、求出现次数最多的次数
2、求出现次数最多的模式串
传送门
多组输入: 当n==0时退出。 注意每次用时clear清空

AC代码:

#include<bits/stdc++.h>
#define maxn 1000001
using namespace std;
struct kkk{
	int son[26];
	int flag; ///相同模式串出现次数 
    int fail;  /// 失配指针
    void clear(){
		memset(son, 0, sizeof(son));
		fail = flag = 0;
	}
}trie[maxn];
int n, cnt,ans;
string s;
string a[200];
int vis[200];
void insert(string s,int num){
	int u = 1;
	int len = s.length();
	for (int i = 0; i < len;i++){
		int v = s[i] - 'a';
		if(!trie[u].son[v]){
			trie[u].son[v] = ++cnt;
		}
		u = trie[u].son[v];
	}
	// 存位置 
	trie[u].flag=num;
}
void getFail(){
	for (int i = 0; i < 26;i++){
		trie[0].son[i] = 1;
	}
	queue<int> q;
	q.push(1);
	trie[1].fail = 0;
	while(!q.empty()){
		int fa = q.front();
		//cout<<fa<<endl;
		q.pop();
		int fafail = trie[fa].fail;
		for (int i = 0; i < 26;i++){
			int v = trie[fa].son[i];
            if(!v){
				trie[fa].son[i] = trie[fafail].son[i];
				continue;
			}
			trie[v].fail = trie[fafail].son[i];
			q.push(v);
		}
	}
}
int query(string s){
	int u = 1, ans = 0;
	int len = s.length();
	for (int i = 0; i < len;i++){
		int v = s[i] - 'a';
		int k = trie[u].son[v];
		while(k>1){
			// 找到了模式串就对该串的vis++ 
			if(trie[k].flag){
				vis[trie[k].flag]++;
			}
			k = trie[k].fail;
		}
		u = trie[u].son[v];
	}
	return ans;
}
void clear(){
	for (int i = 0; i <= cnt;i++){
		trie[i].clear();
	}
	for (int i = 1; i <= n;i++){
		vis[i] = 0;
	}
		cnt = 1;
	ans = 0;
}
void solve(){
	cnt = 1;
    while(1){
		scanf("%d",&n);
		if(n==0)
			break;
		clear();
		for (int i = 1; i <= n;i++)
		{
			cin >> a[i];
			insert(a[i], i);
		}
		getFail();
		cin >> s;
		query(s);
		for (int i = 1; i <= n;i++){
			ans = max(vis[i], ans);
		}
		cout << ans<<endl;
		for (int i = 1; i <= n;i++){
               if(vis[i]==ans){
				   cout << a[i] << endl;
			   }
		}
	}
	return  ;
}
int main(){
	freopen("in.txt", "r", stdin);
  	freopen("out.txt","w", stdout);
	solve();
	return 0;
}

解释
sa[排名]=后缀起始下标
rak[后缀下标] = 排名
height[排名] = 与前一个排名的字符串前缀重复数。
输入:
ababa
输出:
height: 0 1 3 0 2
sa: 5 3 1 4 2
rak: 3 5 2 4 1

倍增法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int wa[N],wb[N],wv[N],wss[N],rak[N],height[N],cal[N],n,sa[N];
char s[N];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int M) {
     int i,j,p,*x=wa,*y=wb,*t;
     for(i=0;i<M;i++) wss[i]=0;
     for(i=0;i<n;i++) wss[x[i]=r[i]]++;
     for(i=1;i<M;i++) wss[i]+=wss[i-1];
     for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;
     for(j=1,p=1;p<n;j*=2,M=p) {
        for(p=0,i=n-j;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0;i<n;i++) wv[i]=x[y[i]];
        for(i=0;i<M;i++) wss[i]=0;
        for(i=0;i<n;i++) wss[wv[i]]++;
        for(i=1;i<M;i++) wss[i]+=wss[i-1];
        for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
        x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
     }
     return;
}
void calheight(int *r,int *sa,int n) {
     int i,j,k=0;
     for(i=1;i<=n;i++) rak[sa[i]]=i;
     for(i=0;i<n;height[rak[i++]]=k)
     for(k?k--:0,j=sa[rak[i]-1];r[i+k]==r[j+k];k++);
     for(int i=n;i;i--)rak[i]=rak[i-1],sa[i]++;
}
int main(){
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
    int cas=1;
    while(~scanf("%s",s+1)){
        n=strlen(s+1);
        for(int i=1;i<=n;i++)
            cal[i]=s[i];
        cal[n+1]=0;
        da(cal+1,sa,n+1,200);
        calheight(cal+1,sa,n);
        cout<<"height: ";
        for(int i=1;i<=n;i++) {
            printf("%d ",height[i]);
        }puts("");
        cout<<"sa: ";
        for(int i=1;i<=n;i++) {
            printf("%d ",sa[i]);
        }puts("");
        cout<<"rak: ";
        for(int i=1;i<=n;i++) {
            printf("%d ",rak[i]);
        }puts("");
    }
} 

DC3

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3*(1e5+5);
int wa[N], wb[N], ws[N], wv[N], sa[N];
int rak[N], height[N], cal[N],n;
char s[N],ans[N],s1[N];
int cnt[N];
int c0(int *r, int a, int b) {
    return r[a] == r[b] && r[a + 1] == r[b + 1] && r[a + 2] == r[b + 2];
}
int c12(int k, int *r, int a, int b) {
    if (k == 2)
        return r[a] < r[b] || r[a] == r[b] && c12(1, r, a + 1, b + 1);
    return r[a] < r[b] || r[a] == r[b] && wv[a + 1] < wv[b + 1];
}
void Rsort(int *r, int *a, int *b, int n, int m) {
    for (int i = 0; i < n; i++) wv[i] = r[a[i]];
    for (int i = 0; i < m; i++) ws[i] = 0;
    for (int i = 0; i < n; i++) ws[wv[i]]++;
    for (int i = 1; i < m; i++) ws[i] += ws[i - 1];
    for (int i = n - 1; i >= 0; i--) b[--ws[wv[i]]] = a[i];
}
void dc3(int *r, int *sa, int n, int m) {
    int i, j, *rn = r + n, *san = sa + n, ta = 0, tb = (n + 1) / 3, tbc = 0, p;
    r[n] = r[n + 1] = 0;
    for (i = 0; i < n; i++) if (i % 3 != 0) wa[tbc++] = i;
    Rsort(r + 2, wa, wb, tbc, m);
    Rsort(r + 1, wb, wa, tbc, m);
    Rsort(r, wa, wb, tbc, m);
    for (p = 1, rn[F(wb[0])] = 0, i = 1; i < tbc; i++)
        rn[F(wb[i])] = c0(r, wb[i - 1], wb[i]) ? p - 1 : p++;
    if (p < tbc) dc3(rn, san, tbc, p);
    else for (i = 0; i < tbc; i++) san[rn[i]] = i;
    for (i = 0; i < tbc; i++) if (san[i] < tb) wb[ta++] = san[i] * 3;
    if (n % 3 == 1) wb[ta++] = n - 1;
    Rsort(r, wb, wa, ta, m);
    for (i = 0; i < tbc; i++) wv[wb[i] = G(san[i])] = i;
    for (i = 0, j = 0, p = 0; i < ta && j < tbc; p++)
        sa[p] = c12(wb[j] % 3, r, wa[i], wb[j]) ? wa[i++] : wb[j++];
    for (; i < ta; p++) sa[p] = wa[i++];
    for (; j < tbc; p++) sa[p] = wb[j++];
    //for(int i=n;i;i--) rak[i]=rak[i-1];
    //for(int i=n;i;i--) sa[i]++;
}
void calheight(int *r, int *sa, int n) {
    int i, j, k = 0;
    for (i = 1; i <= n; i++) rak[sa[i]] = i;
    for (i = 0; i < n; height[rak[i++]] = k)
        for (k ? k-- : 0, j = sa[rak[i] - 1]; r[i + k] == r[j + k]; k++);
    for(int i=n;i;i--) rak[i]=rak[i-1];
    for(int i=n;i;i--) sa[i]++;
}
int main() {
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
    while (~scanf("%s", s+1)) {
        n = strlen(s+1);
        for (int i = 1; i <= n; i++)
            cal[i] = s[i] - 'a' + 1;
        cal[n+1] = 0;
        dc3(cal+1, sa, n + 1, 30);
        calheight(cal+1, sa, n);
        //cout<<"height: ";
        for(int i=1;i<=n;i++) {
            printf("%d ",height[i]);
        }puts("");
        //cout<<"sa: ";
        for(int i=1;i<=n;i++) {
            printf("%d ",sa[i]);
        }puts("");
        //cout<<"rak: ";
        for(int i=1;i<=n;i++) {
            printf("%d ",rak[i]);
        }puts("");
        
    }
    return 0;
}  

输入:
ababa
输出:
height: 0 1 3 0 2
sa: 5 3 1 4 2
rak: 3 5 2 4 1

参考内容:
hyfhaha:AC自动机讲解超详细

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

axtices

谢谢您的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值