E - Acesrc and String Theory 19多校 (后缀数组)

Acesrc is a famous string theorist at Nanjing University second to none. He insists that we should always say an important thing k times. He also believes that every string that can be obtained by concatenating k

copies of some nonempty string is splendid. So, he always teaches newcomers, ``String theory problems are important! String theory problems are important! ... String theory problems are important!"

Today, he wants to examine whether the newcomers remember his instruction. He presents a string consisting of lower case letters and asks them the number of splendid substrings of the presented string. No one can solve this problem, and they will be scolded for hours. Can you help them solve this problem?

Note that equal splendid substrings occurred in different positions should be counted separately.

Input

The first line of input consists of a single integer T

(1≤T≤10)

, denoting the number of test cases. Each test case starts with a single line of an integer k (1≤k≤20). The second line of a test case is a string S consisting of lower case letters only, the length of which is between 1 and 3×105 inclusive. The sum of the lengths of strings in all test cases never exceeds 106

.

Output

For each test case, print the answer as a single integer in a single line.

Sample Input

3
2
aabb
2
abababab
3
abc

Sample Output

2
6
0

题目大意:询问多少个子串是可以通过k次循环得到的。

解题思路:考虑直接枚举长度为k的倍数的所有子串,显然时间复杂度过高。
注意到所有子串都满足存在一个最小循环节,k=1

的情况除外,这时特判即可。
考虑对每个串直接求最小循环节,显然也会超时。
转换思路:

  • 求出所有以x
为循环节的串,那么考虑枚举x,然后设立i,i+x⋯
  • 这些关键点。
  • 依次考虑相邻的两个关键点,通过后缀数组求出lcp,lcs
  • ,得到覆盖当前关键点的串。
  • 那么串对答案的贡献为len−kx+1
/*
@Author: Top_Spirit
@Language: C++
*/
#include <bits/stdc++.h>
using namespace std ;
typedef unsigned long long ull ;
typedef long long ll ;
const int Maxn = 3e5 + 10 ;
const int INF = 0x3f3f3f3f ;
const double PI = acos(-1.0) ;
const int seed = 133 ;

struct Node {
    int sa[Maxn] ;
    int t1[Maxn], t2[Maxn], c[Maxn] ;
    int Rank[Maxn], height[Maxn] ;
    int dp[Maxn][20], lok[Maxn] ;

    void build_sa(char s[], int n, int m){
        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] = s[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 ;
        }
    }

    void getHeight(char s[],int n){
        int i, j, k = 0 ;
        for(i = 0; i <= n; i++) Rank[sa[i]] = i ;
        for(i = 0; i < n; i++){
            if(k) k-- ;
            j = sa[Rank[i] - 1] ;
            while(s[i + k] == s[j + k]) k++ ;
            height[Rank[i]] = k ;
        }
    }

    void initRmq(int n, int b[]){
        lok[0] = -1 ;
        for (int i = 1; i <= n; i++){
            lok[i] = ((i & (i - 1)) == 0) ? lok[i - 1] + 1 : lok[i - 1] ;
            dp[i][0] = b[i] ;
        }
        for (int j = 1; j <= lok[n]; j++){
            for(int i = 1; i + (1 << j) - 1 <= n; i++){
                dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][ j - 1]) ;
            }
        }
    }

    int getRmq(int l, int r){
        int k = lok[r - l + 1] ;
        return min(dp[l][k], dp[r - (1 << k) + 1][k]) ;
    }

    int lcp (int l, int r){
        int L = Rank[l] ;
        int R = Rank[r] ;
//        cout << L << " " << R << endl ;
        if (L > R) swap(L, R) ;
        return getRmq(L + 1, R) ;
    }

}L, R;

char t[Maxn];
char s[Maxn] ;

int main(){
    int T, k ;
    scanf("%d", &T) ;
    while(T--){
        scanf("%d", &k) ;
        ll ans = 0 ;
        scanf("%s",s) ;
        int len = strlen(s);
        for(int i = 0; i < len; i++) t[i] = s[len - i - 1] ;
        t[len] = '\0' ;
        if (k == 1) {
            ans = 1ll * (1 + len) * len / 2 ;
            printf("%lld\n", ans) ;
            continue ;
        }
        L.build_sa(s, len + 1, 520) ;
        L.getHeight(s, len) ;
        L.initRmq(len, L.height) ;
        R.build_sa(t, len + 1, 520) ;
        R.getHeight(t, len) ;
        R.initRmq(len, R.height) ;
        int last ;
        for (int j = 1; j < len; j++){
            last = 0 ;
            for (int i = last + j; i < len; i += j){
                int r = i + L.lcp(last, i) - 1 ;
                if (r >= i + j || r >= len) continue ;
                int l = last - R.lcp(len - i - 1, len - last - 1) + 1 ;
                int num = r - l + 1 ;
                ans += max(0, num - k * j + 1) ;
                last = i ;
            }
        }
        printf("%lld\n", ans) ;
    }
    return 0;
}

/*

*/

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值