• 3277: 串


    3277: 串

    https://www.lydsy.com/JudgeOnline/problem.php?id=3277

    分析:

    广义后缀自动机是什么?

    广义后缀自动机不就是把很多串的SAM建到了一个SAM上,建每个串的时候都从root开始(last=root)就行了。

    广义后缀自动机是Trie树的后缀自动机,可以解决多主串问题。

                                  ——candy?

    首先一个结论:在后缀自动机上:每个状态表示的串的个数为len[i] - len[fa[i]。

    这道题我们可以预处理出后缀自动机上每个节点所表示的串 是 哪些主串的子串,记录cnt[i]。如果cnt[i]>=k那么这个节点表示的所有串,会给包含它的主串增加贡献,贡献为Len[i]-len[fa[i]]。

    然后枚举每个串的一个前缀,这时只要求出这个前缀有多少个后缀满足>=k就好了。

    枚举的每个前缀,它都可以在自动机上走一个点,如果这个点满足>=k,那当前点的所有后缀都可以为它提供贡献,那么就是parent树上其所有祖先和它的和。所以要预先dfs一下每个点的值。

    代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long LL;
     4 
     5 inline int read() {
     6     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
     7     for (;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
     8 }
     9 
    10 const int N = 200100;
    11 
    12 struct SuffixAutomaton{
    13     int Last, Index, fa[N], trans[N][26], len[N];
    14     int n, k, cnt[N], cur[N];
    15     bool vis[N];
    16     string s[N];
    17     char ss[N];
    18     
    19     void extend(int c) {
    20         int P = Last, NP = ++Index;
    21         len[NP] = len[P] + 1;
    22         for (; P&&!trans[P][c]; P=fa[P]) trans[P][c] = NP;
    23         if (!P) fa[NP] = 1;
    24         else {
    25             int Q = trans[P][c];
    26             if (len[P] + 1 == len[Q]) fa[NP] = Q;
    27             else {
    28                 int NQ = ++Index;
    29                 fa[NQ] = fa[Q];
    30                 len[NQ] = len[P] + 1;
    31                 memcpy(trans[NQ], trans[Q], sizeof trans[Q]);
    32                 fa[Q] = NQ;
    33                 fa[NP] = NQ;
    34                 for (; P&&trans[P][c]==Q; P=fa[P]) trans[P][c] = NQ;
    35             }
    36         }
    37         Last = NP;
    38     }
    39     void build() {
    40         Index = 1;
    41         for (int i=1; i<=n; ++i) { // 广义后缀自动机的构建 
    42             Last = 1;
    43             scanf("%s",ss);
    44             s[i] = string(ss);
    45             for (int j=0,L=s[i].length(); j<L; ++j) 
    46                 extend(s[i][j] - 'a');
    47         }
    48     }
    49     void dfs(int u) {
    50         if (u == 1 || vis[u]) return ;
    51         vis[u] = true;
    52         dfs(fa[u]);
    53         cnt[u] += cnt[fa[u]];
    54     }
    55     void solve() {
    56         n = read(),k = read();
    57         build();        
    58         for (int i=1; i<=n; ++i) {
    59             int u = 1;
    60             for (int j=0,L=s[i].length(); j<L; ++j) {
    61                 u = trans[u][s[i][j]-'a']; // 第i个串的一个前缀 
    62                 for (int p=u; p&&cur[p]!=i; p=fa[p]) 
    63                     cur[p] = i, cnt[p] ++;// cnt 当前状态是几个串的子串 
    64             }
    65         }
    66         for (int i=1; i<=Index; ++i) cnt[i] = (cnt[i] >= k) * (len[i] - len[fa[i]]);
    67         memset(vis, false, sizeof(vis));
    68         for (int i=1; i<=Index; ++i) if (!vis[i]) dfs(i);
    69         for (int i=1; i<=n; ++i) {
    70             int ans = 0, u = 1;
    71             for (int j=0,L=s[i].length(); j<L; ++j) 
    72                 u = trans[u][s[i][j]-'a'], ans += cnt[u];
    73             printf("%d ",ans);
    74         }
    75     }
    76 }sam;
    77 
    78 int main() {
    79     sam.solve();
    80     return 0;
    81 }
    View Code

    SA的做法:

    任何一个子串都是一个后缀的前缀i,考虑枚举每一个后缀j,计算它有多少个前缀是满足的。这里可以二分k。

    然后就是判断从有多少个串包含s[i:j],同样的只要看这些串的后缀就行了,由于height数组是单调的,所以再次二分一个mid,表示向前mid个串时候满足之间的height最小值>=k,同样向后也二分一个mid出来,那么就是[L,R]这段区间的任意一个后缀和s[i:]的lcp>=k了,现在求出[L,R]中间有多少不同的串即可,此处可以扫描线预处理出每个i左边第一个满足的包含k个不同串的位置。复杂度$nlog^2n$

    fail树的做法?

    后缀树的做法?

  • 相关阅读:
    9 *9 乘法表
    总结day04 ---- 列表的切片,增删改查,以及,相关方法, 元祖的使用方法
    三级菜单 -----待学习,待强化
    day04 --class --homework
    购物车项目 复杂版本.待简化
    python 学习资料 常用
    总结day3 ---- 进制转换,字符串切片,字符串常用方法.,for 循环,
    day03 --class --homework
    总结day2 ---- while循环的简单使用, 格式化输出.运算符.以及编码的应用
    Uva 10054 欧拉回路 打印路径
  • 原文地址:https://www.cnblogs.com/mjtcn/p/9342725.html
Copyright © 2020-2023  润新知