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.
 

 

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
 

 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:  6205 6204 6203 6202 6201 
 
分析:题目中求只出现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;
}


 

代码如下:
 
posted @ 2017-09-15 14:49  hinata_hajime  阅读(154)  评论(0编辑  收藏  举报