字典树与AC自动机训练赛题解

培训的时候忘讲了,字典树的第一维和标记数组,fail指针数组的大小都应该开成 模式串长*模式串数量

I. hdu1251 统计难题

这题唯一要注意的一点字典树的第一维要开到1e6,不过题目里不给范围也是挺毒的了

#include<bits/stdc++.h>
using namespace std;
const int maxn  = 1e6+7;
struct trie{
    int tr[maxn][26];
    int exits[maxn];
    int tot=0;
    void insert(string str){
        int len = str.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = str[i]-'a';
            if(!tr[p][c])tr[p][c] = ++tot;
            p = tr[p][c];
            exits[p]++;
        }
    }
    int query(string str){
        int len = str.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = str[i] - 'a';
            if(!tr[p][c])return 0;
            p = tr[p][c];
        }
        return exits[p];
    }
}t;
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    string str;
    while(getline(cin,str)){
        if(!str.length())break;
        t.insert(str);
    }
    while(cin>>str){
        cout<<t.query(str)<<endl;
    }
}

J hdu1277 全文检索

这题如果按题目给的范围算的话应该是ac自动机的题,结果好像数据水了,map或者字典树暴力都能过。。好像也没什么要好讲的,看代码就明白了。

既然字典树暴力能过咱也放个字典树的吧,不过我的字典树暴力跑了900多ms,在超时的边缘疯狂试探。

字典树:

#include<bits/stdc++.h>
#define buff ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int maxn = 1e4*60+7;
vector<int> ans;
struct trie{
    int tr[maxn][10];
    int exits[maxn];
    int tot = 0;

    void insert(string str,int id){
        int len = str.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = str[i]-'0';
            if(!tr[p][c])tr[p][c] = ++tot;
            p = tr[p][c];
        }
        exits[p]=id;
    }
    void query(string str){
        int len = str.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = str[i]-'0';
            if(!tr[p][c])return;
            p = tr[p][c];
            if(exits[p]){
                ans.push_back(exits[p]);
                exits[p]=0;
                return ;
            }
        }
    }

}Trie;
int main(){
    buff;
    int n,m;
    cin>>n>>m;
    string text;
    string lin;
    for(int i=0;i<n;i++){
        cin>>lin;
        text+=lin;
    }
    int id;
    for(int i=0;i<m;i++){
        cin>>lin>>lin>>lin;
        id=0;
        for(int i=0;lin[i]!=']';i++){
            id*=10;id+=(lin[i]-'0');
        }
        cin>>lin;
        Trie.insert(lin,id);
    }
    int len = text.length();
    for(int i=0;i<len;i++){
        string str = text.substr(i,len-i);
        Trie.query(str);
    }
    if(ans.size()){
        cout<<"Found key:";
        for(auto i:ans){
            cout<<" [Key No. "<<i<<"]";
        }
    }
    else{
        cout<<"No key can be found!";
    }
    cout<<endl;
}

ac自动机:

#include<bits/stdc++.h>
#define buff ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int maxn = 1e4*60+7;
vector<int> ans;
struct AC{
    int trie[maxn][10];
    int exits[maxn],fail[maxn];
    int tot=0;

    void insert(string s,int id){
        int len = s.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = s[i]-'0';
            if(!trie[p][c])trie[p][c] = ++tot;
            p = trie[p][c];
        }
        exits[p] = id;
    }
    void build(){
        queue<int> q;
        for(int i=0;i<9;i++){
            if(trie[0][i]){
                fail[trie[0][i]] = 0;
                q.push(trie[0][i]);
            }
        }
        while(!q.empty()){
            int p = q.front();
            q.pop();
            for(int i=0;i<10;i++){
                if(trie[p][i]){
                    fail[trie[p][i]] = trie[fail[p]][i];
                    q.push(trie[p][i]);
                }
                else{
                    trie[p][i] = trie[fail[p]][i];
                }
            }
        }
    }
    void query(string str){
        int len = str.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = str[i]-'0';
            p = trie[p][c];
            for(int j=p;j&&exits[p];j=fail[j]){
                ans.push_back(exits[p]);
                exits[p]=0;
            }
        }
    }
}ac;
int main(){
    buff;
    int n,m;
    cin>>n>>m;
    string text;
    string lin;
    for(int i=0;i<n;i++){
        cin>>lin;
        text+=lin;
    }
    int id;
    for(int i=0;i<m;i++){
        cin>>lin>>lin>>lin;
        id=0;
        for(int i=0;lin[i]!=']';i++){
            id*=10;id+=(lin[i]-'0');
        }
        cin>>lin;
        ac.insert(lin,id);
    }
    ac.build();
    ac.query(text);
    if(ans.size()){
        cout<<"Found key:";
        for(auto i:ans){
            cout<<" [Key No. "<<i<<"]";
        }
    }
    else{
        cout<<"No key can be found!";
    }
    cout<<endl;
}

K. poj3764 The xor-longest Path

这题是蓝书上的例题,大家自己看蓝书即可

在这里插入图片描述
在这里插入图片描述
还有这题对时间复杂度和空间复杂度要求都挺高,需要减少不必要的初始化

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#define buff ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int maxn = 1e5+7;
    int tr[maxn*32][2];
    int tot,ans;
    void init1(){
        tot=0;
        ans=0;
        tr[0][0]=0,tr[0][1]=0;
    }
    void insert(int x){
        int p = 0;
        for(int i=31;i>=0;i--){
            bool c = x>>i&1;
            if(!tr[p][c]){
                tr[p][c] = ++tot;
                tr[tot][0]=0;
                tr[tot][1]=0;
            }
            p = tr[p][c];
        }
    }
    void query(int x){
        int p = 0;
        int lin = 0;
        for(int i=31;i>=0;i--){
            bool c = x>>i&1;
            if(tr[p][c^1]){
                p = tr[p][c^1];
                lin += pow(2,i);
            }
            else{
                p = tr[p][c];
            }
        }
        if(lin>ans)ans=lin;
    }





    int head[maxn*2],ver[maxn*2],edge[maxn*2],nex[maxn*2];
    int vis[maxn];
    int d[maxn];
    int tot1=0;
    void init(){
        memset(head,0,sizeof head);
        memset(vis,0,sizeof vis);
        tot1=0;
    }
    void add(int x,int y,int z){
        ver[++tot1]=y,edge[tot1]=z;
        nex[tot1]=head[x],head[x]=tot1;
    }
    void dfs(int x){
        vis[x]=1;
        for(int i=head[x];i;i=nex[i]){
            if(!vis[ver[i]]){
                d[ver[i]] = d[x]^edge[i];
                dfs(ver[i]);
            }
        }
    }
int main(){
    int n;
    while(~scanf("%d",&n)){
        init1();
        init();
        int x,y,z;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&x,&y,&z);
            x++,y++;
            add(x,y,z);
            add(y,x,z);
        }
        dfs(1);
        for(int i=1;i<=n;i++){
            insert(d[i]);
            query(d[i]);
        }
        printf("%d\n",ans);
     }
}

hdu2222 Keywords Search

模板题

如果出现各种错误一般是空间没开好,没初始化或者别的细节

#include<bits/stdc++.h>
using namespace std;
const int N = (1e4+7)*50;
char str[N*2];

struct AC {

int tr[N][26],tot;
int end[N],fail[N]; //数组名叫end不太好反正

void insert(char *s){
	int u=0;
	int l=strlen(s);
	for(int i=0;i<l;i++){
		int c=s[i]-'a';
		if(!tr[u][c])
		   	tr[u][c]=++tot;
		u=tr[u][c];
	}
	end[u]++;
}
void build(){
	queue<int> q;
	for(int i=0;i<26;i++){
		if(tr[0][i]){
			fail[tr[0][i]]=0;
			q.push(tr[0][i]);
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<26;i++){
			if(tr[u][i]){
				fail[tr[u][i]]=tr[fail[u]][i];
				q.push(tr[u][i]);
			}
			else{
				tr[u][i]=tr[fail[u]][i];
			}
		}
	}
}
int query(char *t){
	int l=strlen(t);
	int pos=0,res=0;
	for(int i=0;i<l;i++){
		int c=t[i]-'a';
		pos=tr[pos][c];
		for(int j=pos;j&&end[j]!=-1;j=fail[j]){
			res+=end[j];
			end[j]=-1;
		}
	}
	return res;
}
void init(){
	tot=0;
	memset(end,0,sizeof(end));
	memset(tr,0,sizeof(tr));
}
}ac;

int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		ac.init();
		int n;
		scanf("%d",&n);
		for(int i=0;i<n;i++)
			scanf("%s",str),ac.insert(str);
		ac.build();
		scanf("%s",str);
		printf("%d\n",ac.query(str));
	}
	return 0;
}

M hdu2896 病毒侵袭

这题有点卡空间,不能乱用memset

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+7;
int sum=0;
struct AC{
    int trie[maxn][130];
    int exits[maxn],fail[maxn];
    int tot=0;

    void insert(string s,int id){
        int len = s.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = s[i];
            if(!trie[p][c])trie[p][c]=++tot;
            p = trie[p][c];
        }
        exits[p]=id;
    }
    void build(){
        queue<int> q;
        for(int i=0;i<128;i++){
            if(trie[0][i]){
                fail[trie[0][i]]=0;
                q.push(trie[0][i]);
            }
        }
        while(!q.empty()){
            int p = q.front();
            q.pop();
            for(int i=0;i<128;i++){
                if(trie[p][i]){
                    fail[trie[p][i]] = trie[fail[p]][i];
                    q.push(trie[p][i]);
                }
                else{
                    trie[p][i] = trie[fail[p]][i];
                }
            }
        }
    }
    void query(string str,int id){
        vector<int> ans;
        ans.clear();
        int len = str.length();
        int p = 0;
        for(int i=0;i<len;i++){
            int c = str[i];
            p = trie[p][c];
            for(int j=p;j&&exits[j];j=fail[j]){
                ans.push_back(exits[j]);
            }
        }
        sort(ans.begin(),ans.end());
        if(ans.size()){
            sum++;
            cout<<"web "<<id<<":";
            for(auto i:ans){
                cout<<" "<<i;
            }
            cout<<endl;
        }
    }
}ac;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n,m;
    cin>>n;
    string lin;
    for(int i=1;i<=n;i++){
        cin>>lin;
        ac.insert(lin,i);
    }
    ac.build();
    cin>>m;
    for(int i=1;i<=m;i++){
        cin>>lin;
        ac.query(lin,i);
    }
    cout<<"total: "<<sum<<endl;
}

N POJ 2778 DNA Sequence

这题的精髓在于利用矩阵进行计数,感觉以我的水平搞的不是很清楚,给大家一些很牛逼的资源吧:

  1. 去年学长给我们讲解AC自动机时的课件,清晰准确,对本题还特别做了讲解,强烈推荐!

    点这里下载学长的课件

  2. kuangbin大佬的题解

    点这是去看kuangbin的题解

  3. csdn上某大佬的博客

    点这里去看大佬的博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值