AC自动机

初步学了下,一些优化还没学会,做了一道模板题和两道变式,最后一道想尽办法优化还是有4个测试点超时…先贴上来吧,回头想办法。
简单版

就纯粹套模板

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstdlib>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<set>
#define fi first
#define se second
#define pb push_back
#define me(a,b) memset(a,b,sizeof(a))
#define INIT() std::ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>P;
const int MAX_N=1000000+5;
const int INF=0x7fffffff;
const int inf=100000;
const double EPS=1e-6;
const ull base=123;
const ll mod=1e9+7;
const double pi=4*atan(1.0);
int tree[MAX_N][30];
int color[MAX_N];
int cnt=1;
int fail[MAX_N];
void build(string s)
{
   
    int root=0;
    int i;
    for(i=0;i<s.size();i++)
    {
   
        int x=s[i]-'a';
        if(!tree[root][x])
        {
   
            tree[root][x]=cnt++;
        }
        root=tree[root][x];
    }
    color[root]++;
}
void buildfail()
{
   
    int root=0;
    queue<int>q;
    int i;
    for(i=0;i<26;i++)
    {
   
        if(tree[root][i])
        {
   
            fail[tree[root][i]]=0;
            q.push(tree[root][i]);
        }
    }
    while(!q.empty())
    {
   
        int now=q.front();
        q.pop();
        for(i=0;i<26;i++)
        {
   
            if(tree[now][i])
            {
   
                fail[tree[now][i]]=tree[fail[now]][i];
                q.push(tree[now][i]);
            }
            else
            {
   
                tree[now][i]=tree[fail[now]][i];
            }
        }
    }
}
int Find(string s)
{
   
    int now=0;
    int ans=0;
    int i;
    for(i=0;i<s.size();i++)
    {
   
        now=tree[now][s[i]-'a'];
        for(int j=now;j&&color[j]!=-1;j=fail[j])
        {
   
            ans+=color[j];
            color[j]=-1;
        }
    }
    return ans;
}
int main()
{
   
    int n;
    cin>>n;
    string s;
    while(n--)
    {
   

        cin>>s;
        build(s);
    }
    buildfail();
    cin>>s;
    int ans=Find(s);
    cout<<ans<<endl;
}

加强版
这题有个小小的变式,因为是要找出每个字符串的出现次数,所以这里不需要color数组了,把每个字符串末尾的节点标记上相应字符串的编号,最后查询的时候每经过这个节点,就把其对应的字符串编号加一,代表出现了一次,最后直接遍历字符串找最大就行。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstdlib>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<set>
#define fi first
#define se second
#define pb push_back
#define me(a,b) memset(a,b,sizeof(a))
#define INIT() std::ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>P;
const int MAX_N=1000000+5;
const int INF=0x7fffffff;
const int inf=100000;
const double EPS=1e-6;
const ull base=123;
const ll mod=1e9+7;
const double pi=4*atan(1.0);
int tree[MAX_N][30];
int e[MAX_N];
int cnt=1;
int fail[MAX_N];
int ans[155];
string s[155];
string c;
void build(string s,int num)
{
   
    int root=0;
    int i;
    for(i=0; i<s.size(); i++)
    {
   
        int x=s[i]-'a';
        if(!tree[root][x])
        {
   
            tree[root][x]=cnt++;
        }
        root=tree[root][x];
    }
    e[root]=num;
}
void buildfail()
{
   
    int root=0;
    queue<int>q;
    int i;
    for(i=0; i<26; i++)
    {
   
        if(tree[root][i])
        {
   
            fail[tree[root][i]]=0;
            q.push(tree[root][i]);
        }
    }
    while(!q.empty())
    {
   
        int now=q.front();
        q.pop();
        for(i=0; i<26; i++)
        {
   
            if(tree[now][i])
            {
   
                fail[tree[now][i]]=tree[fail[now]][i];
                q.push(tree[now][i]);
            }
            else
            {
   
                tree[now][i]=tree[fail[now]][i];
            }
        }
    }
}
void Find(string s)
{
   
    int now=0;
    int i;
    for(i=0; i<s.size(); i++)
    {
   
        now=tree[now][s[i]-'a'];
        for(int j=now; j; j=fail[j])
        {
   
            int u=e[j];
            ans[u]++;
        }
    }
    return;
}
int main()
{
   
    int n;
    INIT();
    while(cin>>n&&n)
    {
   
        me(tree,0);
        me(e,0);
        me(fail,0);
        me(ans,0);
        int i;
        for(i=1; i<=n; i++)
        {
   
            cin>>s[i];
            build(s[i],i);
        }
        buildfail();
        cin>>c;
        Find(c);
        int mx=0;
        for(i=1;i<=n;i++)
        {
   
            mx=max(mx,ans[i]);
        }
        cout<<mx<<endl;
        for(i=1;i<=n;i++)
        {
   
            if(ans[i]==mx)
                cout<<s[i]<<endl;
        }

    }

}

二次加强
这题题意和上面一题没什么区别,就是不用求最大,把所有字符串出现次数输出就行,但是这题用同样的方法时间复杂度过不去,我是认为问题出在fail[]上,因为每次查询的时候都是暴力跳fail,而在跳的过程中很有可能所对应的节点不是字符串末端,所以会浪费复杂度,因此我前面加了个g[],在创造fail的时候直接判断下一个是不是末端,是的话g[i]=fail[i],不是就g[i]=g[fail[i]],这样查询的时候直接跳g就行了,然而,还是有几个点过不去…

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstdlib>
#include<string>
#include<vector>
#include<list>
#include<map>
#include<set>
#define fi first
#define se second
#define pb push_back
#define me(a,b) memset(a,b,sizeof(a))
#define INIT() std::ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>P;
const int MAX_N=2000000+5;
const int INF=0x7fffffff;
const int inf=100000;
const double EPS=1e-6;
const ull base=123;
const ll mod=1e9+7;
const double pi=4*atan(1.0);
int tree[MAX_N][30];
vector<int>e[MAX_N];
int cnt=1;
int fail[MAX_N];
int g[MAX_N];
int ans[200005];
string s;
string c;
void build(string s,int num)
{
   
    int root=0;
    int i;
    for(i=0; i<s.size(); i++)
    {
   
        int x=s[i]-'a';
        if(!tree[root][x])
        {
   
            tree[root][x]=cnt++;
        }
        root=tree[root][x];
    }
    e[root].pb(num);
}
void buildfail()
{
   
    int root=0;
    queue<int>q;
    int i;
    for(i=0; i<26; i++)
    {
   
        if(tree[root][i])
        {
   
            fail[tree[root][i]]=0;
            q.push(tree[root][i]);
        }
    }
    while(!q.empty())
    {
   
        int now=q.front();
        q.pop();
        for(i=0; i<26; i++)
        {
   
            if(tree[now][i])
            {
   
                fail[tree[now][i]]=tree[fail[now]][i];
                int u=tree[fail[now]][i];
                if(e[u].size())
                    g[tree[now][i]]=u;
                else
                    g[tree[now][i]]=g[u];
                q.push(tree[now][i]);
            }
            else
            {
   
                tree[now][i]=tree[fail[now]][i];
            }
        }
    }
}
void Find(string s)
{
   
    int now=0;
    int i;
    for(i=0; i<s.size(); i++)
    {
   
        now=tree[now][s[i]-'a'];
        for(int j=now; j; j=g[j])
        {
   
            for(int u=0;u<e[j].size();u++)
            {
   
                //cout<<e[j][u]<<endl;
                ans[e[j][u]]++;
            }
        }
    }
    return;
}
int main()
{
   
    int n;
    INIT();
    cin>>n;
    int i;
    for(i=1; i<=n; i++)
    {
   
        cin>>s;
        build(s,i);
    }
    buildfail();
    cin>>c;
    Find(c);
    for(i=1; i<=n; i++)
    {
   
        cout<<ans[i]<<endl;
    }
}

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务