string string string
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1531 Accepted Submission(s): 440
Problem Description
Uncle Mao is a wonderful ACMER. One day he met an easy problem, but Uncle Mao was so lazy that he left the problem to you. I hope you can give him a solution.
Given a string s, we define a substring that happens exactly k times as an important string, and you need to find out how many substrings which are important strings.
Given a string s, we define a substring that happens exactly k times as an important string, and you need to find out how many substrings which are important strings.
Input
The first line contains an integer
T
(
T≤100
) implying the number of test cases.
For each test case, there are two lines:
the first line contains an integer k ( k≥1 ) which is described above;
the second line contain a string s ( length(s)≤105 ).
It's guaranteed that ∑length(s)≤2∗106 .
For each test case, there are two lines:
the first line contains an integer k ( k≥1 ) which is described above;
the second line contain a string s ( length(s)≤105 ).
It's guaranteed that ∑length(s)≤2∗106 .
Output
For each test case, print the number of the important substrings in a line.
Sample Input
2 2 abcabc 3 abcabcabcabc
Sample Output
6 9
Source
Recommend
思路:我做这道题纯碎来学一下后缀数组怎么用。。。。首先构造好数组之后,每次枚举相邻k个排名的公共前缀,因为他要刚好出现k次,所以容斥减去出现k+1次再加上k+2次的就是贡献。。查询rmq预处理可以O(1),查询log应该也可以过。下面给代码:
#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cmath>
#include<queue>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<string>
#include<utility>
#include<set>
#include<map>
#include<stack>
#include<vector>
#define maxn 100005
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long LL;
const double eps = 1e-8;
const int mod = 1e9 + 7;
const int MAXN = 100005;
int SA[MAXN], Rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m;
int q;
int f[MAXN][20];
char str[MAXN];
//rank[i] 第i个后缀的排名; SA[i] 排名为i的后缀位置; Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//tax[i] 计数排序辅助数组; tp[i] rank的辅助数组(计数排序中的第二关键字),与SA意义一样。
//a为原串
void RSort() {
//rank第一关键字,tp第二关键字。
for (int i = 0; i <= m; i++) tax[i] = 0;
for (int i = 1; i <= n; i++) tax[Rank[tp[i]]] ++;
for (int i = 1; i <= m; i++) tax[i] += tax[i - 1];
for (int i = n; i >= 1; i--) SA[tax[Rank[tp[i]]] --] = tp[i]; //确保满足第一关键字的同时,再满足第二关键字的要求
} //计数排序,把新的二元组排序。
int cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w]; }
//通过二元组两个下标的比较,确定两个子串是否相同
void Suffix() {
//SA
for (int i = 1; i <= n; i++) Rank[i] = a[i], tp[i] = i;
m = 30, RSort(); //一开始是以单个字符为单位,所以(m = 127)
for (int w = 1, p = 1, i; p < n; w += w, m = p) { //把子串长度翻倍,更新rank
//w 当前一个子串的长度; m 当前离散后的排名种类数
//当前的tp(第二关键字)可直接由上一次的SA的得到
for (p = 0, i = n - w + 1; i <= n; i++) tp[++p] = i; //长度越界,第二关键字为0
for (i = 1; i <= n; i++) if (SA[i] > w) tp[++p] = SA[i] - w;
//更新SA值,并用tp暂时存下上一轮的rank(用于cmp比较)
RSort(), swap(Rank, tp), Rank[SA[1]] = p = 1;
//用已经完成的SA来更新与它互逆的rank,并离散rank
for (i = 2; i <= n; i++) Rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++p;
}
//离散:把相等的字符串的rank设为相同。
//LCP
int j, k = 0;
for (int i = 1; i <= n; Height[Rank[i++]] = k)
for (k = k ? k - 1 : k, j = SA[Rank[i] - 1]; a[i + k] == a[j + k]; ++k);
//这个知道原理后就比较好理解程序
}
void Init() {
scanf("%d%s", &q, str);
n = strlen(str);
for (int i = 0; i < n; i++) a[i + 1] = str[i] - 'a' + 1;
a[n + 1] = 0;
}
void rmq(){
for (int i = n; i > 1; i--)
{
for (int j = 0; i + (1 << j) - 1 <= n; j++)
{
if (!j)f[i][j] = Height[i];
else f[i][j] = min(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
}
}
int getans(int l, int r){
if (l > r)
return n - SA[r] + 1;
int k = log2(r - l + 1);
return min(f[l][k], f[r - (1 << k) + 1][k]);
}
int main() {
int t;
scanf("%d", &t);
while (t--){
memset(SA, 0, sizeof(SA));
memset(Height, 0, sizeof(Height));
memset(Rank, 0, sizeof(Rank));
memset(tax, 0, sizeof(tax));
memset(tp, 0, sizeof(tp));
Init();
Suffix();
rmq();
LL ans = 0;
for (int i = 1; i <= n - q + 1; i++){
ans += getans(i + 1, i + q - 1);
if (i > 1)
ans -= getans(i, i + q - 1);
if (i + q <= n)
ans -= getans(i + 1, i + q);
if (i > 1 && i + q <= n)
ans += getans(i, i + q);
}
printf("%lld\n", ans);
}
}