gdfzoj #786 很容易AC的题(AC自动机)

标签:AC自动机
原题链接
gdfzoj #786 很容易AC的题(AC自动机)_第1张图片
gdfzoj #786 很容易AC的题(AC自动机)_第2张图片


这道题看上去并不好做,但是如果你学过fail指针,再结合题目标题,一看就知道是用AC自动机。
题目中提及“找到第x个字符串和第y个字符串的连续公共子串同时也是某个字符串的前缀的串”,我们便可以利用fail指针来解题。

考虑到 fail指针的性质:假设有一个节点k,他的失败指针指向j。那么k,j满足这个性质:设root到j的距离为n,则从k之上的第n个节点到k所组成的长度为n的单词,与从root到j所组成的单词相同。

那么对于每一个询问,我们将字符串x和字符串y的每一个字符的fail指针指到过的节点分别用布尔数组记录下来。如果一个节点同时被两个字符串中的字符的fail指针指到,这个点在trie中的深度就可以作为答案,当然,要维护最大值。

时间复杂度为O(n*常数)。
P.S. :这道题有一些细节问题容易忽略或打错。

#include
#include
#include
#include
#include
#include
#include
#define maxn 100050
using namespace std;
struct smg
{
    int fail,w[26],deep;
}a[maxn];
int cnt=0,n,i,j,now,que,wz[maxn],top1,top2,v,u,len1,len2,c;
int head,tail,x,y,ans,l,ii;
bool m1[maxn],m2[maxn];
string s[maxn],s1,s2;
inline void build(string s)
{
    l=s.length(); now=0;
    for (ii=0;iiif (a[now].w[s[ii]-'a']==0) 
        {
            a[now].w[s[ii]-'a']=++cnt;
            a[cnt].deep=ii+1;
        }
        now=a[now].w[s[ii]-'a'];
    }
    wz[i]=now;
}
void getfail()
{
    queue<int> q;
    for(int i=0;i<26;++i)
    {
        if(a[0].w[i]!=0)
        {
            a[a[0].w[i]].fail=0;
            q.push(a[0].w[i]);
        }
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<26;++i)
        {
            if(a[u].w[i]!=0)
            {
                a[a[u].w[i]].fail=a[a[u].fail].w[i];
                q.push(a[u].w[i]);
            }
            else a[u].w[i]=a[a[u].fail].w[i];
        }
    }
}
int main()
{
    scanf("%d",&n);
    for (i=1;i<=n;i++)
    {
        cin>>s[i]; build(s[i]);
    }
    getfail();
    scanf("%d",&que);
    while (que--)
    {
        scanf("%d%d",&x,&y); ans=0; 
        memset(m1,0,sizeof(m1)); memset(m2,0,sizeof(m2));
        s1=s[x]; s2=s[y]; len1=s1.length(); len2=s2.length();
        u=0;
        for (i=0;i'a'; u=a[u].w[c];
            for (v=u;v&&(!m1[v]);v=a[v].fail) m1[v]=1;
        }
        u=0;
        for (i=0;i'a'; u=a[u].w[c];
            for (v=u;v&&(!m2[v]);v=a[v].fail) m2[v]=1;
        }
        for (i=1;i<=cnt;i++)
            if (m1[i]&&m2[i])
                if (a[i].deep>ans) ans=a[i].deep;
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(AC自动机)