POJ 3415 (后缀自动机)

POJ 3415 Common Substrings

Problem : 给两个串S、T (len <= 10^5), 询问两个串有多少个长度大于等于k的子串(位置不同也算)。
Solution :最开始的想法是将S串和T串先后插入后缀自动机,统计出每个节点对应串的出现次数,不过这种做法被卡空间了。
第二种想法是只将S串插入后缀自动机,建立后缀树,统计出每个节点对应串的出现次数,在统计出每个节点的所有父亲节点的出现次数之和。之后将T串在后缀自动机上进行匹配,假设当前T串在p节点匹配成功,且匹配成功长度为len,那么对答案的贡献就是p节点所有长度超过k的父亲节点,再加上当前节点中长度超过k但不超过
tmp的串。

#include <iostream>
#include <string>

using namespace std;

const int N = 200008;

struct edge
{
    int v, nt;
};

struct Suffix_Automanon
{
    int nt[N][60], a[N], fail[N];
    int tot, last, root;
    int lt[N], sum;
    int p, q, np, nq;
    int cnt[N];
    long long f[N];
    edge eg[N];

    int newnode(int len)
    {
        for (int i = 0; i < 60; ++i) nt[tot][i] = -1;
        fail[tot] = -1; cnt[tot] = f[tot] = lt[tot] = 0; a[tot] = len;
        return tot++;
    }
    void clear()
    {
        tot = sum = 0;
        last = root = newnode(0);
    }
    void add(int u, int v)
    {
        eg[++sum] = (edge){v, lt[u]}; lt[u] = sum;
    }
    void insert(int ch)
    {
        p = last; np = last = newnode(a[p] + 1); cnt[np] = 1;
        for (; ~p && nt[p][ch] == -1; p = fail[p]) nt[p][ch] = np;
        if (p == -1) fail[np] = root;
        else
        {
            q = nt[p][ch];
            if (a[p] + 1 == a[q]) fail[np] = q;
            else
            {
                nq = newnode(a[p] + 1);
                for (int i = 0; i < 60; ++i) nt[nq][i] = nt[q][i]; 
                fail[nq] = fail[q]; fail[q] = fail[np] = nq;
                for (; ~p && nt[p][ch] == q; p = fail[p]) nt[p][ch] = nq;
            } 
        }
    }
    void dfs(int u)
    {
        for (int i = lt[u]; i; i = eg[i].nt)
        {
            dfs(eg[i].v);
            cnt[u] += cnt[eg[i].v];
        }
    }
    void dfs(int u, int k)
    {
        for (int i = lt[u]; i; i = eg[i].nt)
        {
            if (u != root && k <= a[u])
            {  
                f[eg[i].v] += f[u] + (a[u] - max(k, a[fail[u]] + 1) + 1) * cnt[u];
            }
            dfs(eg[i].v, k);
        }
    }
    void build(int k)
    {
        for (int i = 1; i < tot; ++i) add(fail[i], i);
        dfs(root);
        dfs(root, k);
    }
    void solve(const string &s, int k)
    {
        int p = root, tmp = 0;
        long long ans = 0;
        for (int i = 0, len = s.length(); i < len; ++i)
        {
            int ch = s[i] - 'A';
            if (~nt[p][ch]) p = nt[p][ch], tmp++;
            else
            {
                for (; ~p && nt[p][ch] == -1; p = fail[p]);
                if (p == -1) p = root, tmp = 0;
                else
                {
                    tmp = a[p] + 1;
                    p = nt[p][ch]; 
                }
            }
            if (p != root)
            {
                ans += f[p];
                if (tmp >= k && k <= a[p]) ans += (min(a[p], tmp) - max(k, a[fail[p]] + 1) + 1) * cnt[p];
            }
        }
        cout << ans << endl;
    }

}sam;

int main()
{
    int n; string s, t;
    while (cin >> n >> s >> t)
    {
        sam.clear();
        for (int i = 0, len = s.length(); i < len; ++i)
            sam.insert(s[i] - 'A');
        sam.build(n);
        sam.solve(t, n);
    }
}

转载于:https://www.cnblogs.com/rpSebastian/p/7222439.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值