hdu 6194 后缀数组+RMQ

string string string

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 616    Accepted Submission(s): 171


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  ( T100 ) implying the number of test cases.
For each test case, there are two lines:
the first line contains an integer  k  ( k1 ) which is described above;
the second line contain a string  s  ( length(s)105 ).
It's guaranteed that  length(s)2106 .
 

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


题意:给出一个字符串,问其中出现k次的子串的个数有多少个


思路:后缀数组的题,在字符串中出现k次的话,那么在后缀数组中,连续的k个后缀数组的height数组的最小值肯定不为0,但是我们也不能全加,全加的话可能会多加,其中一些字符串出现的次数可能大于>k也被加进去了,解决这个问题的话我们只需要再减去max(height[i],height[i+k])就可以了

需要解决的问题

1.区间最小值

线段树或者是RMQ

2.枚举区间

直接枚举区间左端点

还要注意一点就是注意端点的问题,我们要在height数组的结尾加一个0做端点


ac代码:

#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <set>
#include <iostream>
#include <cmath>
using namespace std;
const int N = 200005;
int sa[N],s[N],wa[N], wb[N], ws1[N], wv[N];
int rank1[N], height[N],mi[100005][30],Log2[N];
char ss[N];
void init(int n)
{
     for(int i = 0; i <=n; i ++)Log2[i] = (i == 0 ? -1 : Log2[i >> 1] + 1);
}
bool cmp(int r[], int a, int b, int l)
{
    return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(int r[], int sa[], int n, int m)
{
    int i, j, p, *x = wa, *y = wb;
    for (i = 0; i < m; ++i) ws1[i] = 0;
    for (i = 0; i < n; ++i) ws1[x[i]=r[i]]++;
    for (i = 1; i < m; ++i) ws1[i] += ws1[i-1];
    for (i = n-1; i >= 0; --i) sa[--ws1[x[i]]] = i;
    for (j = 1, p = 1; p < n; j *= 2, m = p)
    {
        for (p = 0, 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 < n; ++i) wv[i] = x[y[i]];
        for (i = 0; i < m; ++i) ws1[i] = 0;
        for (i = 0; i < n; ++i) ws1[wv[i]]++;
        for (i = 1; i < m; ++i) ws1[i] += ws1[i-1];
        for (i = n-1; i >= 0; --i) sa[--ws1[wv[i]]] = y[i];
        for (swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
    }
}

void calheight(int r[], int sa[], int n)
{
    int i, j, k = 0;
    for (i = 1; i <= n; ++i) rank1[sa[i]] = i;
    for (i = 0; i < n; height[rank1[i++]] = k)
        for (k?k--:0, j = sa[rank1[i]-1]; r[i+k] == r[j+k]; k++);
}
void rmq_isit(int n)
{
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
                mi[i][j]=min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
        }
    }
}
int query(int x,int y)
{
    int k=Log2[y-x+1];
    return min(mi[x][k],mi[y-(1<<k)+1][k]);
}
int sc(int i,int k,int len)
{
    if(k==1) return len-sa[i];
    return query(i+1,i+k-1);
}
int main()
{
    int t,k;cin>>t;
    while(t--)
    {
        int ans=0;
        scanf("%d%s",&k,ss);
        int len=strlen(ss);
        for(int i=0;i<len;i++)
        s[i]=ss[i]-'a'+1;
        s[len]=0;
        da(s,sa,len+1,28);
        calheight(s,sa,len);
        height[len+1]=0;
        for(int i=1;i<=len;i++)
        mi[i][0]=height[i];
        init(len);
        rmq_isit(len);
        for(int i=1;i<=len-k+1;i++)
        ans+=max(sc(i,k,len)-max(height[i],height[i+k]),0);
        printf("%d\n",ans);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值