给一个串,求问有多少子串是由k个循环节组成的。
似乎是一种经典的搞循环节的做法…
对字符串进行枚举分块(从1到len/k枚举块的个数),然后判断有多少个连续的块是相同的。
得到了一片相同的块之后,我们再把这一段向左右扩展,对这几块前面的前缀部分,与这几块的任意一部分求个lcs(最长公共后缀),lcs的长度即为可向前拓展的部分。同理,对这几块后面的后缀与任意一块求一个lcp(最长公共前缀),lcp长度即为可拓展的部分,这样就找到了当前能找到的最长一个循环串。
这个串对答案的贡献是max(0,(lcp+lcs+1)+当前相同块个数 乘 当前块长 - k 乘 当前块长)
那么现在重点就变成了如何求任意两个后缀的lcp和lcs,如果用后缀数组求,非常的好写…
如果用sam写,你就要求两个节点lca…就非常恶心,而且dfs求dfs序的时候还会爆栈(parent树是一条链时深度=串长,比如aaaaaaa),于是要用栈模拟dfs。
然而你权神就是要写sam,下面这份令人绝望的代码就诞生了。
权神说要被夸!权神的rmqnb!
#include
#define X first
#define Y second
//#pragma GCC optimize(2)
using namespace std;
typedef unsigned long long ll;
const int maxn = 3e5 + 5;
int _, k, len;
char s[maxn];
int Log[maxn << 2];
struct Sam {
int next[maxn << 1][26];
int link[maxn << 1], step[maxn << 1];
int pos[maxn];
vector<int> G[maxn << 1];
int sz, last, root;
void init() {
//如多次建立自动机,加入memset操作
for (int i = 0; i <= sz; ++i) {
G[i].clear();
}
root = sz = last = 1;
memset(next[root], 0, sizeof(next[root]));
}
void add(int c) {
int p = last;
int np = ++sz;
last = np;
memset(next[np], 0, sizeof(next[np]));
step[np] = step[p] + 1;
while (!next[p][c] && p) {
next[p][c] = np;
p = link[p];
}
if (p == 0) {
link[np] = root;
} else {
int q = next[p][c];
if (step[p] + 1 == step[q]) {
link[np] = q;
} else {
int nq = ++sz;
memcpy(next[nq], next[q], sizeof(next[q]));
step[nq] = step[p] + 1;
link[nq] = link[q];
link[q] = link[np] = nq;
while (next[p][c] == q && p) {
next[p][c] = nq;
p = link[p];
}
}
}
}
int in[maxn << 1], tot;
int p[maxn << 2], dep[maxn << 1];
void dfs(int x, int fa) {
in[x] = ++tot;
p[tot] = x;
for (int i = 0; i < G[x].size(); ++i) {
int v = G[x][i];
dep[v] = dep[x] + 1;
dfs(v, x);
}
// p[++tot]=fa;
// for (auto v:G[x]) {
// dep[v] = dep[x] + 1;
// dfs(v);
// p[++tot] = x;
// }
}
void dfss() {
stack<pair<int, int>> st;
st.push({root, 0});
in[root] = ++tot;
p[tot] = root;
while (!st.empty()) {
pair<int, int> x = st.top();
st.pop();
if (x.Y == G[x.X].size()) {
if (st.size() > 0)
p[++tot] = st.top().X;
continue;
}
st.push({x.X, x.Y + 1});
st.push({G[x.X][x.Y], 0});
in[G[x.X][x.Y]] = ++tot;
dep[G[x.X][x.Y]] = st.size() - 1;
p[tot] = G[x.X][x.Y];
}
}
int rmq[maxn << 2][30];
int cmp(int p1, int p2) {
if (dep[p1] < dep[p2]) return p1;
return p2;
}
void rbq() {
for (int i = 1; i < tot; i++) {
rmq[i][0] = cmp(p[i], p[i + 1]);
}
for (int i = 1; i <= 29; i++) {
for (int j = 1; j <= tot; j++) {
if ((1 << i - 1) + j > tot) break;
rmq[j][i] = cmp(rmq[j][i - 1], rmq[(1 << i - 1) + j][i - 1]);
}
}
}
int getlca(int a, int b) {
if (in[a] > in[b])
swap(a, b);
int l = in[b] - in[a];
if (l == 0)
return 1;
int p = Log[l];
return step[cmp(rmq[in[a]][p], rmq[in[b] - (1 << p)][p])];
}
void build() {
init();
tot = 0;
for (int i = 0; s[i]; i++) {
add(s[i] - 'a');
pos[i] = last;
}
for (int i = sz; i > root; --i) {
G[link[i]].push_back(i);
}
// memset(p,0, sizeof(p));
// memset(dep,0, sizeof(dep));
memset(in,0,sizeof(in));
// dfs(root, 0);
dfss();
rbq();
}
} s1, s2;
void solve() {
ll ans = 0;
for (int i = 1; i <= len / k; ++i) {
int lcs = 0, cnt = 1, lcp;
for (int j = 0; j < len; j += i) {
if (j + i > len)
lcp = 0;
else
lcp = s2.getlca(s2.pos[j], s2.pos[j + i]);
if (lcp >= i) {
++cnt;
} else {
ans += max(lcp + lcs + 1 + cnt * i - k * i, 0);
cnt = 1;
if (j + 2 * i - 1 > len)
lcs = 0;
else
lcs = s1.getlca(s1.pos[j + i - 1], s1.pos[j + 2 * i - 1]);
}
}
}
printf("%lld\n", ans);
}
/*
1
2
abababab*/
int main() {
scanf("%d", &_);
Log[0] = -1;
for (int i = 1; i < maxn * 4; i++) {
Log[i] = Log[i / 2] + 1;
}
while (_--) {
scanf("%d%s", &k, s);
len = strlen(s);
if(k == 1){
printf("%lld\n",1LL * len * (len + 1) / 2);
continue;
}
s1.build();
reverse(s, s + len);
s2.build();
reverse(s2.pos, s2.pos + len);
solve();
}
return 0;
}