HDU 6194 string string string( 后缀数组+RMQ)
string string string
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1462 Accepted Submission(s): 420
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次的子串.
可以用后缀数组来进行处理,每次找出 minheight(i,i+k-2)-max(height[i-1],height[i+k-1])即出现次数达到k的子串数量-次数大于k的子串数量
这里当k=1的时候特殊处理,在后缀i中,出现达到一次的子串数量为 len-sa[i]+1 次数大于k的子串数量为 max(height[i],height[i+1])
len-sa[i]+1-max(height[i],height[i+1])累加即可
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAXN=2e5+10;
int wa[MAXN],wb[MAXN],wv[MAXN],Ws[MAXN];
char str[MAXN];
int minsum[MAXN][20];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(const char r[],int sa[],int n,int m) //n为len+1,m一般比数组中最大的数大一点即可
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0; i<m; i++) Ws[i]=0;
for(i=0; i<n; i++) Ws[x[i]=r[i]]++;
for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
for(i=n-1; i>=0; i--) sa[--Ws[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++) Ws[i]=0;
for(i=0; i<n; i++) Ws[wv[i]]++;
for(i=1; i<m; i++) Ws[i]+=Ws[i-1];
for(i=n-1; i>=0; i--) sa[--Ws[wv[i]]]=y[i];
for(t=x,x=y,y=t,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++;
}
return;
}
int sa[MAXN],Rank[MAXN],height[MAXN];// sa是通过后缀排名找到它在字符串中的位置,rank是根据位置找到后缀排名,两者相逆,该模板中sa数组的最小值为1。
void calheight(const char *r,int *sa,int n)
{
int i,j,k=0;
for(i=1; i<=n; i++) Rank[sa[i]]=i;
for(i=0; i<n; height[Rank[i++]]=k)
for(k?k--:0,j=sa[Rank[i]-1]; r[i+k]==r[j+k]; k++);
for(int i=n;i>=1;--i) ++sa[i],Rank[i]=Rank[i-1];
}
void RMQ_In(int num) //预处理->O(nlogn)
{
for(int j = 1; j < 20; ++j)
for(int i = 1; i <= num; ++i)
if(i + (1 << j) - 1 <= num)
{
minsum[i][j] = min(minsum[i][j - 1], minsum[i + (1 << (j - 1))][j - 1]);
}
}
int RMQ_Query(int src,int des)
{
int minn;
int k=(int)(log(des-src+1.0)/log(2.0));
minn=min(minsum[src][k],minsum[des-(1<<k)+1][k]);
return minn;
}
long long Find1(int len,int k)
{
k--;
long long res=0;
int l=1,r=k;
}
int main()
{
int t,len;
ll sum,sum2;
ll n;
scanf("%d",&t);
while(t--)
{
int maxx=0;
scanf("%lld",&n);
scanf("%s",str);
len=strlen(str);
// memset(minsum,0,sizeof(minsum));
memset(height,0,sizeof(height));
da(str,sa,len+1,130);
calheight(str,sa,len);
height[len+1]=height[0]=height[1]=0;
for(int i=0;i<=len;i++)
minsum[i][0]=height[i];
RMQ_In(len);
ll ans=0;
if(n==1)
{
for(int i=1;i<=len;i++)
{
ans+=len-sa[i]+1-max(height[i],height[i+1]);
}
printf("%lld\n",ans);
continue;
}
for(int i=2;i<=len+1;i++)
{
if(i+n-2<=len)
if(RMQ_Query(i,i+n-2)-max(height[i-1],height[i+n-1])>0)
ans+=RMQ_Query(i,i+n-2)-max(height[i-1],height[i+n-1]);
}
if(ans<0)while(1);
printf("%lld\n",ans);
}
return 0;
}
代码如下: