给定一个字符串和 k k k,问有多少个子串是由某个串连接k次组成的。
枚举循环节长度 l l l,将字符串的每 l l l个字符分成一段,如果两段完全相同,则它们属于同一段。
如果段末尾之后一些字符和段首部的一些字符相同,则将其也加入段中,依此法将段朝前后尽量扩展,这里需要快速地求两个后缀的最长公共前缀,以及两个前缀的最长公共后缀,可以使用两次后缀数组实现。
如果扩展完成后的段长度大于等于 k ∗ l k*l k∗l,那么此段中所有 k ∗ l k*l k∗l的子串都是符合条件的子串,答案加 l e n − k ∗ l + 1 len-k*l+1 len−k∗l+1。不同的段可能会有重叠部分。
注意,这个方法无法解决 k = 1 k=1 k=1的情况,直接输出 n ∗ ( n + 1 ) / 2 n*(n+1)/2 n∗(n+1)/2即可。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 300016, MOD = 1000000007;
struct SA
{
int _n;
/* 后缀数组 */
int sa[M], ra[M], height[M]; //后缀三数组,sa和ra下标从0开始,height下标从1开始
int t1[M], t2[M], c[M]; // 用于基数排序的三个辅助数组
void build(char *str, int n, int m) // 构造后缀三数组,字符串下标从0开始,n表示长度,m表示字符集大小
{
n++;
int i, j, p, *x = t1, *y = t2;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[i]=str[i]]++;
for(i = 1; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;
for(j = 1; j <= n; j<<=1)
{
p = 0;
for(i = n-j; i < n; i++) y[p++] = i;
for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i]-j;
for(i = 0; i < m; i++) c[i] = 0;
for(i = 0; i < n; i++) c[x[y[i]]]++;
for(i = 1; i < m; i++) c[i] += c[i-1];
for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1; x[sa[0]] = 0;
for(i = 1; i < n; i++)
x[sa[i]] = (y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]) ? p-1 : p++;
if(p >= n) break;
m = p;
}
n--;
for(int i = 0; i <= n; i++) ra[sa[i]] = i;
for(int i=0, j=0, k=0; i < n; i++)
{
if(k) k--;
j = sa[ra[i]-1];
while(str[i+k]==str[j+k]) k++;
height[ra[i]] = k;
}
st_init(height, n);
}
/* ST表 */
int lg[M];
int table[20][M];
void st_init(int *arr, int n)
{
_n = n;
if(!lg[0])
{
lg[0]=-1;
for(int i=1;i<M;i++)
lg[i]=lg[i/2]+1;
}
for(int i=1; i<=n; ++i)
table[0][i] = arr[i];
for(int i=1; i<=lg[n]; ++i)
for(int j=1; j<=n; ++j)
if(j+(1<<i)-1 <= n)
table[i][j] = min(table[i-1][j], table[i-1][j+(1<<(i-1))]);
}
// 查询第l个后缀和第r个后缀的LCP,下标从0开始
int lcp(int l, int r)
{
l = ra[l], r = ra[r];
if(l>r) swap(l,r);
++l;
int t = lg[r-l+1];
return min(table[t][l], table[t][r-(1<<t)+1]);
}
// 查询第l个前缀和第r个前缀的最长公共后缀,此时字符串是反的,下标从0开始
// 实际上就是查询第n-l-1个后缀和第n-r-1个后缀的LCP
int lcs(int l, int r)
{
return lcp(_n-l-1,_n-r-1);
}
}sa1, sa2;
char save[M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int t = read();
while(t--)
{
int k = read(), n;
scanf("%s",save);
n = strlen(save);
if(k==1)
{
cout << 1ll*n*(n+1)/2<<endl;
continue;
}
sa1.build(save, n, 128);
reverse(save, save+n);
sa2.build(save, n, 128);
ll ans = 0;
for(ll l=1; k*l<=n; ++l)
{
//printf("l=%I64d\n",l );
for(int i=0,j; i+l-1<n; i=(j+1)/l*l) //i表示段起点
{
j = i+l-1 + sa1.lcp(i,i+l); //j表示段右边界
if(i>=l) i = i - sa2.lcs(i+l-1,i-1); //i表示段左边界
// printf("%d %d\n",i,j );
ans += max((j-i+1)-l*k+1, 0ll);
}
}
cout << ans << endl;
}
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}