后缀数组poj2774 poj3415

  【问题描述】 
 
  给出两个字符串T和P,请你用后缀数组完成下列任务:

  任务1、计算这两个字符串的最长公共子串,并输出字典序最小的公共串。

  任务2、计算长度不小于K的公共子串的个数(子串相同但位置不同算不同的子串)。
  例如:T="xx",P="xx",K=1,则长度不小于K 的公共子串的个数是5。
  再如:T="aababaa",P="abaabaa",K=2,则长度不小于K 的公共子串的个数是22。
    
  【输入】 
 
  含3行,第一行是字符串T,第二行是字符串P,第3行一个整数K。
    
  【输出】 
 
  含3行:
  第1行一个整数,表示T和P的最长公共子串长度。
  第2行一个字符串,表示T和P字典序最小的公共子串。
  第3行一个字符串,表示T和P长度不小于K的公共子串数目。
  

分别对应任务1和任务2的答案。
    
  【样例输入】 
 
aababaa
abaabaa
2
    
  【样例输出】 
 
4
aaba
22
    
  【数据范围】 
 
字符串长度不超过100 000,1<=K<=100 000

 

 

 

 

【样例解释】

 

 


  任务1、"aababaa"和"abaabaa",的最长公共子串为"aaba"或"abaa";

  任务2、"aababaa"和"abaabaa",长度不小于2的不同公共子串有:
  1、"aa" 共有4个;
  2、"ab" 共有4个;
  3、"ba" 共有4个;
  4、"aab" 共有1个;
  5、"aba" 共有4个;
  6、"baa" 共有2个;
  7、"aaba" 共有1个;
  8、"abaa" 共有2个;

  总共4+4+4+1+4+2+1+2=22个。

poj2774 poj3415

 

【问题分析】

task1:

可以等价于求两串所有后缀的最长公共前缀。

加一个奇葩的分隔符将两串连接起来,并标记新串s中的每个字符s[i](s[i]属于T则将i标记为1,s[i]属于P则将i标记为2,s[i]为分隔符则标记为0),求后缀数组,求height数组。

显然,如果求一个串的最长重复子串(可重叠),则ans=max(height[i]),但本题要求不是同一个字符串的两个后缀,所以更新答案前先判断一下sa[i]与sa[i-1]是否分属T,P即可。

 

task2:

对于P,T的两个后缀i,j,若LCP(i,j)=l,则对答案的贡献为l-k+1。

按task1连接成新串后,按height分组,每组内相邻后缀的height>=K,显然不同组之间是相互独立的,对于每一组,要求出所有P之前的T与之LCP-K+1的和,以及所有T之前的P与之的LCP-K+1的和,累加到ans中,显然暴力枚举的时间复杂度为O(N^2)。

经分析,这又是两个等价问题。现在只考虑求出所有P在同一组内之前的所有T与之LCP-K+1的和。

对于任意一个P来说,在它前面的T与之的LCP是单调不减的。从左到右扫描sa,当前元素为i,设h=height[i+1]-K+1为当前i与P0(i右边同一组元素中最近的P)的答案贡献。设一个单调递增的栈stack,stack[top].h为栈顶元素与当前P0的答案贡献,stack[top].x为答案贡献为stack[top].h的T元素有多少个。设sum=本组内当前所有扫过的T与P0的总贡献。

若当前元素i为P,ans=ans+sum;

将当前元素加入栈中,维护sum;

 

#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
       
         #include 
        
          #include 
         
           #include 
          
            #include 
           
             #include 
            
              #define MAXN 100010 using namespace std; typedef long long LL; //task1:加一个奇葩的分隔符将两串连接起来。分属两串 的height中最大值即为所求 //task2:对每个P(T)统计前面的T(P)的LCP-K+1之和。扫两遍height //快速统计:维护height的单调递增 栈(对P统计T为例) //sum为当前组内所有’T‘对当前元素的LCP-K+1之和 //当前元素为'P',累加ans //将当前元素的height及id加入单调栈,维护sum char T[MAXN],P[MAXN],s[MAXN<<1]; int K,n; int old[MAXN<<1]; int sa[MAXN<<1],tsa[MAXN<<1],c[MAXN<<1],x[MAXN<<1],y[MAXN<<1],z[MAXN<<1]; int rank[MAXN<<1],height[MAXN<<1]; void getsa(char *s,int n,int m) { int i,p; for(i=0;i 
             
               =0;i--) sa[--c[s[i]]]=i; for(int k=1;k<=n;k<<=1){ for(i=0;i 
              
                =0;i--) sa[--c[x[tsa[i]]]]=tsa[i]; z[sa[0]]=p=1; for(i=1;i 
               
                 >T>>P>>K; int i; n=0; for(i=0;T[i];i++){ old[n]=1; s[n++]=T[i]; } old[n]=0; s[n++]=125; for(i=0;P[i];i++){ old[n]=2; s[n++]=P[i]; } getsa(s,n,150); getheight(s,n); } LL ans; char maxpub[MAXN<<1],t[MAXN<<1]; void task1() { ans=0; for(int i=1;i<=n;i++){ int p=sa[i],q=sa[i-1]; if(old[p]!=old[q]){ if(ans 
                
                  =h){ sum+=((LL)h-stack[top].h)*stack[top].x; x+=stack[top].x; top--; } if(old[p]==A) sum+=h; stack[++top]=(Stack){h,x}; } } void task2() { ans=0; deal(1,2); deal(2,1); cout< 
                 
                   < 
                   
                  
                 
                
               
              
             
            
           
          
         
       
      
      
     
     
    
    
   
   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值