017 ACM/ICPC Asia Regional Shenyang Online 1001 后缀数组+单调队列

string string string

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


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

1.所有子串,考虑后缀数组解
2.画出后缀数组
aabaaab
  aaab
2aab
3aabaaab
1ab
2abaaab
0b
1baaab
3.观察,设某个子串下标为[i],考虑 如果重复K次 那么该序列存在 k-1个子串的最小值为x,即min(height[i],height[i+1]...height[i+k-2]); 设两端点最大值为 y=max(height[i-1],height[i+k-1]),那么对于这K个子串,恰好重复k次的子串为 mex(i,i+y+1),mex(i,i+y+2)...mex(i,i+y+x);
4.维护 x值 ,即为rmq问题,可以采用线段树,st,树状数组等算法,这里可以考虑单调队列,实现简单且O(n)算法。

下面贴出代码

#include <iostream>
#include <cstring>
#include <queue>
#include <cstdio>
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
#define maxn 101000
using namespace std;
typedef long long ll;
int wa[maxn],wb[maxn],wv[maxn],wss[maxn];
int r[maxn*3],sa[maxn*3],height[maxn],ranks[maxn];

int c0(int *r,int a,int b)
{
    return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b)
{
    if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
    else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];
}
void sort(int *r,int *a,int *b,int n,int m)
{
    int i;
    for(i=0;i<n;i++) wv[i]=r[a[i]];
    for(i=0;i<m;i++) wss[i]=0;
    for(i=0;i<n;i++) wss[wv[i]]++;
    for(i=1;i<m;i++) wss[i]+=wss[i-1];
    for(i=n-1;i>=0;i--) b[--wss[wv[i]]]=a[i];
    return;
}
void dc3(int *r,int *sa,int n,int m)
{
    int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    r[n]=r[n+1]=0;
    for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
    sort(r+2,wa,wb,tbc,m);
    sort(r+1,wb,wa,tbc,m);
    sort(r,wa,wb,tbc,m);
    for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
    rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
    if(p<tbc) dc3(rn,san,tbc,p);
    else for(i=0;i<tbc;i++) san[rn[i]]=i;
    for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
    if(n%3==1) wb[ta++]=n-1;
    sort(r,wb,wa,ta,m);
    for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
    for(i=0,j=0,p=0;i<ta && j<tbc;p++)
    sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
    for(;i<ta;p++) sa[p]=wa[i++];
    for(;j<tbc;p++) sa[p]=wb[j++];
    return;
}
void calheight(int *r,int *sa,int n)
{
    memset(ranks,0,sizeof(ranks));
    int i,j,k=0;
    for(i=1;i<=n;i++) ranks[sa[i]]=i;
    for(i=0;i<n;height[ranks[i++]]=k)
    for(k?k--:0,j=sa[ranks[i]-1];r[i+k]==r[j+k];k++);
    return;
}
char s[maxn];
struct node{
    int x;
    int index;
    node(){
    }
    node(int x,int index){
        this->x=x;
        this->index=index;
    }
};
deque <node> q;
int ans[maxn];
ll slove(int k,int n){
    q.clear();
    memset(ans,0,sizeof(ans));
    if(k!=1){
    for(int i = 2 ; i<=k ; i++){
        while(!q.empty()&&height[i]<=q.back().x){
            q.pop_back();
        }
        q.push_back(node(height[i],i));
    }
    ans[2]=q.front().x;
    for(int i = k+1 ; i<=n ; i++){
        while(!q.empty()&&height[i]<=q.back().x){
            q.pop_back();
        }
        q.push_back(node(height[i],i));
        while(q.front().index<=(i-k+1)){
            q.pop_front();
        }
        ans[i-k+2]=q.front().x;
    }
    ll num=0;
    for(int i = 2 ; i<=n-k+2 ; i++){
        int b=height[i+k-1];
        if(i+k-1>n){
            b=0;
        }
        int c=max(height[i-1],b);
        if(ans[i]>c){
            num+=(ans[i]-c);
        }
    }
    return num;
    }
    else{
        ll num=0;
        for(int i = 1 ; i<=n ; i++){
            ans[i]=n-sa[i];
        }
        for(int i = 1 ; i<=n ; i++){
            int a=height[i];
            int b=height[i+1];
            if(i==n){
                b=0;
            }
            if(ans[i]-max(a,b)>0)
            num+=(ans[i]-max(a,b));
        }
        return num;
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int k;
        scanf("%d",&k);
        scanf("%s",s); m
        int len=strlen(s);
        memset(sa,0,sizeof(sa));
        memset(r,0,sizeof(r));
        for(int i = 0 ; s[i] ; i++){
            r[i]=s[i];

        }
        dc3(r,sa,len+1,256);
        calheight(r,sa,len);
        printf("%lld\n",slove(k,len));
    }
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值