[Nowcoder 2018ACM多校第一场I] substring

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/81178521

题目大意:
给一个串S, 求其不同构的子串个数。 ( | S | 5 10 4 , | s i | = a , b , c )

题目思路:
由于字符集大小只有3, 考虑所有的映射情况只有6种。 所以可以将原串通过映射得到6份, 求这几个串不同子串个数。 考虑同构的情况, 对于一般的子串, ‘abc’, ‘acb’, ‘bac’, ‘bca’, ‘cab’, ‘cba’会被算6次, 但是对于单字符子串’aa’, ‘bb’, ‘cc’只会被算3次。 故答案是(不同子串个数+单字符不同子串个数)*3/6。

PS: 关于求多个串的不同子串个数
将多个串用分隔符拼接成一个新串, 建立后缀自动机。 后缀自动机接受所有的子串, 可以用记忆化搜索, 求出每个点接受的子串个数(不走分隔符的边)。 单字符子串个数则从根开始一直走‘a’, 走到的长度就是个数。
还可以有一种更简单的方法, 不用分隔符拼接, 在插入完第i个串的字符, 要插入第i+1个串时, 将SAM中的tail重新调至root。 这样得到的后缀自动机直接 s t e p [ i ] s t e p [ p a r e n t [ i ] ] 就是不同子串个数。

Code:

#include <map>
#include <set>
#include <map>
#include <bitset>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

#define ll long long

using namespace std;

const int N = (int)1e6 + 10;

int n;
char str[N];
int f[8][3] = {{}, {0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}};

int cnt, root, step[N * 2], child[N * 2][4], parent[N * 2];
void build()
{

    for (int j = 1; j <= 6; j ++){
        int tail = root;
        for (int i = 1 ; i <= n ; i ++){
            int nxt = str[i + (j - 1) * n] - 'a';
            int nn = ++ cnt, p = tail, q, nq;
            parent[nn] = -1;
            memset(child[nn], -1, sizeof(child[nn]));

            step[nn] = step[p] + 1;
            for (; p != -1 && child[p][nxt] == -1; p = parent[p]) child[p][nxt] = nn;
            if (p == -1) parent[nn] = root; //情况1
            else {
                q = child[p][nxt];
                if (step[p]+1 == step[q]) parent[nn] = q; //情况2
                else {
                    nq = ++cnt;
                    step[nq] = step[p]+1;
                    parent[nq] = parent[q];
                    parent[nn] = parent[q] = nq;
                    for (int j = 0 ; j < 4 ; j ++) child[nq][j] = child[q][j];
                    for (; p != -1 && child[p][nxt] == q ; p = parent[p]) child[p][nxt] = nq;  //情况3
                }
            }
            tail = nn;
        }
    }
}

int main(){
    while (scanf("%d", &n) != EOF){
        ++ tim;
        root = cnt = 0;
        parent[root] = -1;
        memset(child[root], -1, sizeof(child[root]));

        scanf("%s", str + 1);
        int pos = n;
        for (int i = 2; i <= 6; i ++){
            //str[++ pos] = 'd';

            for (int j = 1; j <= n; j ++)
                str[++ pos] = f[i][str[j] - 'a'] + 'a';

        }

        build();

        ll ans = 0;
        for (int i = 1; i <= cnt; i ++) ans += step[i] - step[parent[i]];

        ll m = 0;
        for (int p = root; p != -1; p = child[p][0]) m = step[p];

        printf("%lld\n", (ans + m * 3) / 6);
    }


    return 0;
}

猜你喜欢

转载自blog.csdn.net/u013578420/article/details/81178521