您的位置:首页 > 其它

HDU6194(后缀数组 + rmq)

2017-09-18 15:29 211 查看


string string string

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 1672    Accepted Submission(s): 482


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.

 

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.

 

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

2017 ACM/ICPC Asia Regional Shenyang Online

 

Recommend

liuyiding   |   We have carefully selected several similar problems for you:  6216 6215 6214 6213 6212 

解题思路:后缀数组,先处理出height数组,然后每次取连续的k个前缀,求他们的lcp,就是在这段区间出现k次的字符串的数量,但是这些字符串并不是每个字符串都出现k次,可能会出现大于k次的情况,我们减去这部分就行,在这段区间的上边界和下边界各处理一下就行,我们可以用st表或者线段树维护一下区间lcp最小值就行。注意:每次tp数组或者rrank数组要清0,不然会影响到height的计算。。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100000 + 10;
typedef long long LL;
int kk;
int dp[maxn][30];
int Log[maxn];
char s[maxn];
int n, m;
int a[maxn];//原数组
int rrank[maxn];//第一关键字
int Rank[maxn];//第i个前缀排第几
int tp[maxn];//第二关键字,序号为第二关键字的排名,值为对应的第一关键字的位置
int c[maxn];//用于基数排序用的数组
int Sa[maxn];//后缀数组,排名为i的后缀是哪一个后缀
int Height[maxn];//排名为i的后缀与排名为i - 1的后缀的lcp
bool judge(int *x, int loc, int ww)
{
if(x[Sa[loc]] == x[Sa[loc - 1]] && x[Sa[loc] + ww] == x[Sa[loc - 1] + ww]) return true;
else return false;
}
void suffix()
{
memset(tp, -1, sizeof(tp));//注意,一定要清0
for(int i = 1; i <= n; i++) rrank[i] = a[i];
for(int i = 1; i <= n; i++) tp[i] = i;
for(int i = 0; i <= 128; i++) c[i] = 0;

eff9
for(int i = 1; i <= n; i++) c[rrank[tp[i]]]++;
for(int i = 1; i <= 128; i++) c[i] += c[i - 1];
for(int i = n; i >= 1; i--) Sa[c[rrank[tp[i]]]--] = tp[i];
m = 128;
for(int w = 1, p = 0; w <= n; w += w, m = p)
{
p = 0;
for(int i = n - w + 1; i <= n; i++) tp[++p] = i;

for(int i = 1; i <= n; i++)
{
if(Sa[i] > w) tp[++p] = Sa[i] - w;
}
for(int i = 0; i <= m; i++) c[i] = 0;
for(int i = 1; i <= n; i++) c[rrank[tp[i]]]++;
for(int i = 1; i <= m; i++) c[i] += c[i - 1];
for(int i = n; i >= 1; i--) Sa[c[rrank[tp[i]]]--] = tp[i];
for(int i = 0; i <= n; i++) tp[i] = rrank[i];//重新计算rank的值
rrank[Sa[1]] = 1;
p = 1;
for(int i = 2; i <= n; i++)
{
if(judge(tp, i, w)) rrank[Sa[i]] = p;
else rrank[Sa[i]] = ++p;
}
if(p >= n) break;
}
for(int i = 1; i <= n; i++)
{
Rank[Sa[i]] = i;
}
int k = 0;
Height[1] = 0;
for(int i = 1; i <= n; i++)
{
if(k) k--;
while(a[i + k] == a[Sa[Rank[i] - 1] + k])
{
k++;
}
Height[Rank[i]] = k;
}
}
void initRmq()
{
Log[0] = -1;
for(int i = 1; i <= n; i++)
{
if((i&(i - 1)) == 0) Log[i] = Log[i - 1] + 1;
else Log[i] = Log[i - 1];
dp[i][0] = Height[i];
}
for(int j = 1; j <= 25; j++)
{
for(int i = 1; i <= n && (i + (1<<j) - 1) <= n; i++)
{
dp[i][j] = min(dp[i][j - 1], dp[i + (1<<(j - 1))][j - 1]);
}
}
}
int rmq(int l, int r)
{
int d = r - l + 1;
d = Log[d];
return min(dp[l][d], dp[r - (1<<d) + 1][d]);
}
void init()
{
n = strlen(s);
memset(Height, 0, sizeof(Height));
memset(a, -1, sizeof(a));//注意,一定要重置。在这里wa了无数次
for(int i = 0; i < n; i++)
{
a[i + 1] = s[i];
}
}
int main()
{
//freopen("C:\\Users\\creator\\Desktop\\in1.txt","r",stdin) ;
//freopen("C:\\Users\\creator\\Desktop\\out.txt","w",stdout) ;
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &kk);
scanf("%s", s);
init();
suffix();
initRmq();
LL ans = 0;
if(kk == 1)
{
for(int i = 1; i <= n; i++)
{
LL value = (LL)(n - Sa[i] + 1);
LL value1, value2;
value1 = (LL)Height[i];
if(i + 1 <= n) value2 = (LL)Height[i + 1];
else value2 = 0;
LL Max = max(value1, value2);
if(value > Max) ans += value - Max;
}
}
else
{
for(int i = 1; i + 1 <= n && i + (kk - 1) <= n; i++)
{
LL value = (LL)rmq(i + 1, i + (kk - 1));
LL value1 = (LL)rmq(i, i + (kk - 1));
LL value2;
if(i + kk <= n) value2 = (LL)rmq(i + 1, i + kk);
else value2 = 0;
LL Max = max(value1, value2);
if(value > Max) ans += value - Max;
}
}
printf("%I64d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: