Good Article Good sentence HDU - 4416 (后缀自动机)

Good Article Good sentence

\[
Time Limit: 3000 ms\quad Memory Limit: 32768 kB
\]

题意

给出一个 \(S\) 串,在给出 \(n\) 个 \(T\) 串,求出 \(S\) 串中有多少子串没有在任意一个 \(T\) 串中出现过

思路

\(\quad\) 首先可以对 \(S\) 串构建后缀自动机,然后在插入 \(n\) 个 \(T\) 串,每两个串之间用 \(27\) 隔开,然后可以求出这个自动机上每个节点出现的最左位置 \(left\) 和最右位置 \(right\),然后判断 \(right\) 在 \(Slen\) 内时,这个节点包含的子串就是一个答案!但是!\(MLE\) 了,哭了??,所以我也不知道这个做法能不能过,应该可以
\(\quad\) 正确的做法,先对 \(S\) 串构建后缀自动机,结构体内多开一个变量 \(maxlen\) 表示在 \(i\) 个节点上,和任意一个 \(T\) 串的最长连续公共子串,可以枚举 \(T\) 串,每一个 \(T\) 串用与 \(SPOJ-LCS\) 类似的做法来做。在注意一下子串往 \(father\) 更新的过程,原理与 \(SPOJ-LCS2\) 类似。
\(\quad\) 求出每个节点的 \(maxlen\) 后,就可以开始计算答案了,可以分成两种情况:

  1. maxlen = 0,这个节点内的所有子串都满足条件,满足条件的子串有 \(node[i].len - node[father].len\) 个。
  2. 长度在 [maxlen+1,node[i].len] 区间内的子串满足条件,满足条件的子串有 \(node[i].len - maxlen\) 个。
#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define  lowbit(x)  x & (-x)
#define  mes(a, b)  memset(a, b, sizeof a)
#define  fi         first
#define  se         second
#define  pii        pair<int, int>
#define  INOPEN     freopen("in.txt", "r", stdin)
#define  OUTOPEN    freopen("out.txt", "w", stdout)

typedef unsigned long long int ull;
typedef long long int ll;
const int    maxn = 1e5 + 10;
const int    maxm = 1e5 + 10;
const ll     mod  = 1e9 + 7;
const ll     INF  = 1e18 + 100;
const int    inf  = 0x3f3f3f3f;
const double pi   = acos(-1.0);
const double eps  = 1e-8;
using namespace std;

int n, m;
int cas, tol, T;

struct SAM {
    struct Node {
        int next[27];
        int fa, len;
        int maxlen;
        void init() {
            mes(next, 0);
            fa = len = maxlen = 0;
        }
    } node[maxn<<1];
    vector<int> vv[maxn<<1];
    bool vis[maxn<<1];
    int last, sz;
    void init() {
        last = sz = 1;
        node[sz].init();
    }
    void insert(int k) {
        int p = last, np = last = ++sz;
        node[np].init();
        node[np].len = node[p].len+1;
        for(; p&&!node[p].next[k]; p=node[p].fa)
            node[p].next[k] = np;
        if(p == 0) {
            node[np].fa = 1;
        } else {
            int q = node[p].next[k];
            if(node[q].len == node[p].len + 1) {
                node[np].fa = q;
            } else {
                int nq = ++sz;
                node[nq] = node[q];
                node[nq].len = node[p].len + 1;
                node[np].fa = node[q].fa = nq;
                for(; p&&node[p].next[k]==q; p=node[p].fa)
                    node[p].next[k] = nq;
            }
        }
    }
    void solve(char *s) {
        int len = strlen(s+1);
        int p = 1, tmpans = 0;
        for(int i=1; i<=len; i++) {
            int k = s[i]-'a'+1;
            while(p && !node[p].next[k]) {
                p = node[p].fa;
                tmpans = node[p].len;
            }
            if(p == 0) {
                p = 1;
                tmpans = 0;
            } else {
                p = node[p].next[k];
                tmpans++;
            }
            node[p].maxlen = max(node[p].maxlen, tmpans);
        }
    }
    ll ans;
    void dfs(int u) {
        if(vis[u])  return ;
        vis[u] = true;
        for(auto v : vv[u]) {
            dfs(v);
            node[u].maxlen = min(node[u].len, max(node[u].maxlen, node[v].maxlen));
        }
        if(node[u].maxlen == 0) {
            ans += node[u].len - node[node[u].fa].len;
        } else {
            ans += node[u].len - node[u].maxlen;
        }
    }
    ll calc() {
        ans = 0;
        for(int i=1; i<=sz; i++)    vv[i].clear();
        for(int i=2; i<=sz; i++) {
            vv[node[i].fa].push_back(i);
        }
        mes(vis, 0);
        dfs(1);
        return ans;
    }
} sam;
char s[maxn], t[maxn];

int main() {
    cas = 1;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        scanf("%s", s+1);
        int slen = strlen(s+1);
        sam.init();
        for(int i=1; i<=slen; i++) {
            sam.insert(s[i]-'a'+1);
        }
        for(int i=1; i<=n; i++) {
            scanf("%s", t+1);
            sam.solve(t);
        }
        ll ans = sam.calc();
        printf("Case %d: %lld\n", cas++, ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Jiaaaaaaaqi/p/10947066.html

时间: 2024-08-05 06:54:11

Good Article Good sentence HDU - 4416 (后缀自动机)的相关文章

不在B中的A的子串数量 HDU - 4416 (后缀自动机模板题目)

题目: 给定一个字符串a,又给定一系列b字符串,求字符串a的子串不在b中出现的个数. 题解: 先将所有的查询串放入后缀自动机(每次将sam.last=1)(算出所有子串个数) 然后将母串放入后缀自动机然后记录这个子串个数 两个值相减即可 1 #include <set> 2 #include <map> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include <

HDU 5442 后缀自动机+kmp

题目大意: 给定一个字符串,可理解成环,然后选定一位置,逆时针或顺时针走一遍,希望得到字典序最大,如果同样大,希望找到起始位置最小的,如果还相同,就默认顺时针 比赛一直因为处理最小位置出错,一结束就想明白了...真是作孽 这里正向后缀自动机跑一遍最大的,这样得到的位置肯定是最小的 而逆时针最大就反向重建后缀自动机再跑一遍最大的,但这样因为后缀自动机跑出的位置是最小的,但返回来就变成最大的位置了 如果存在更小的位置,那么就相当于可以从开头拎出一段移到末尾,那么必然是产生一个循环节,这个长度可以利用

hdu 4416 Good Article Good sentence(后缀自动机)

题目链接:hdu 4416 Good Article Good sentence 题意: 给你一个串A和n个串B,问你A有多少个子串不是这n个B的子串. 题解: 将A串建立后缀自动机,对于每个B串都拿去匹配一下,并记录后缀自动机中每个节点的最大匹配长度. 然后拓扑排序,更新每个节点的fail节点.最后对于每个节点的贡献就是ml[i]-max(is[i],mx[f[i]]) (is[i]是该节点的最大匹配长度) 1 #include<bits/stdc++.h> 2 #define F(i,a,

hdu 4416 Good Article Good sentence (后缀数组)

题目大意: 给出一个A串和很多个B串,求出A中有多少个子串,是所有的B中没有出现的. 思路分析: 后缀数组的作用很容易的求出来整个串中不同的子串个数. 现在要求的是A中不同的,且在B中没有出现过的. 先把AB 串全部连接,跑一遍suffix array.然后求出有多少个不同的子串. 然后再单独用B 串跑 suffix array.再求出单独在B 中有多少个不同的 子串. 然后结果就是 ans1 - ans2 ... 需要注意的问题就是,连接的时候需要把每一个串后面加一个特殊符.但是求不同串的时候

hdu 4416 Good Article Good sentence(后缀数组&amp;思维)

Good Article Good sentence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2308    Accepted Submission(s): 649 Problem Description In middle school, teachers used to encourage us to pick up pre

[hdu 4416]Good Article Good sentence

最近几天一直在做有关后缀自动机的题目 感觉似乎对后缀自动机越来越了解了呢!喵~ 这题还是让我受益颇多的,首先搞一个后缀自动机是妥妥的了 可是搞完之后呢? 我们来观察 step 这个变量,每个节点的 step 是从根节点到此节点所经过的最长步数 那么也就是以该点为结尾的最长的后缀长度 如何统计不被 Bi 串包含的子串呢? 其实很简单,维护每个节点所能匹配的最长的字符串长度 然后 节点->step-max(该节点所能匹配的最长的字符串长度, 节点->fail->step) 就是答案了 因为

HDOJ 题目4416 Good Article Good sentence(后缀数组求a串子串在b串中不出现的种类数)

-每周六晚的BestCoder(有米!) Good Article Good sentence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2784    Accepted Submission(s): 785 Problem Description In middle school, teachers used to encour

【后缀自动机】HDU 5343 MZL&#39;s Circle Zhou

通道 题意:从A,B分别取出子串X,Y,求多少种不同的X+Y 思路: 代码: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_N = 200007; typedef unsigned long long ll; struct SAM { int val[MAX_N], fa[MAX_N], c[26][MAX_N]; int tot,

HDU 4622 Reincarnation(后缀自动机)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=4622 [题目大意] 给出一个长度不超过2000的字符串,有不超过10000个询问,问[L,R]子串中出现的子串数目,相同子串不可重复计数. [题解] 考虑到字符串长度只有两千,我们对每个位置往后建立2000个后缀自动机, 这样子就能分别计算每个位置往后出现的字符串数目并保存, 对于sam上的一个节点来说,它的匹配长度与失配位置的匹配长度只差就是他们之间的子串, 所以,我们在建立sam可以同时计算