版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013578420/article/details/81178521
题目大意:
给一个串S, 求其不同构的子串个数。
题目思路:
由于字符集大小只有3, 考虑所有的映射情况只有6种。 所以可以将原串通过映射得到6份, 求这几个串不同子串个数。 考虑同构的情况, 对于一般的子串, ‘abc’, ‘acb’, ‘bac’, ‘bca’, ‘cab’, ‘cba’会被算6次, 但是对于单字符子串’aa’, ‘bb’, ‘cc’只会被算3次。 故答案是(不同子串个数+单字符不同子串个数)*3/6。
PS: 关于求多个串的不同子串个数
将多个串用分隔符拼接成一个新串, 建立后缀自动机。 后缀自动机接受所有的子串, 可以用记忆化搜索, 求出每个点接受的子串个数(不走分隔符的边)。 单字符子串个数则从根开始一直走‘a’, 走到的长度就是个数。
还可以有一种更简单的方法, 不用分隔符拼接, 在插入完第i个串的字符, 要插入第i+1个串时, 将SAM中的tail重新调至root。 这样得到的后缀自动机直接
就是不同子串个数。
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;
}