zoukankan      html  css  js  c++  java
  • 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 ",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 ",ans);

      }
    return 0;
    }


    代码如下:
     
  • 相关阅读:
    Android笔记之调用系统相机拍照
    Android笔记之RoundedImageView
    Java笔记之public、protected、default和private
    Android笔记之ExpandableListView(悬浮吸顶Demo)
    Android笔记之Fragment中创建ViewModel的正确方式
    Android代号、版本及API级别之间的对应关系
    【Phabricator】教科书一般的Phabricator安装教程(配合官方文档并带有踩坑解决方案)
    【Ansible】记一次技术博客害死人的经历——ansible模板变量注入探究
    【linux杂谈】遇到REMOTE HOST IDENTIFICATION HAS CHANGED怎么办?
    【linux杂谈】在SSH连接中,openssh如何解决'Connection refused'错误?
  • 原文地址:https://www.cnblogs.com/a249189046/p/7526422.html
Copyright © 2011-2022 走看看