洛谷3763 TJOI2017 DNA (SA+RMQ)

1 篇文章 0 订阅

题目链接

首先一看到这个题,由于是两个串,所以一定是第一步把两个串拼起来,然后中间加入一个非法字符。

然后我们考虑怎么算这个东西?

由于存在一个可以修改三次的设定。
那我们不妨直接先跑一遍 S A SA SA,然后求出来各个数组。

直接枚举原串中的每一个后缀,然后暴力和后面的匹配。
令一个指针指到当前枚举的后缀,另一个指到第二串的开头,每次可以跳 l c p lcp lcp的长度,遇到一个不同的,就强行跳过。一共可以强行跳过三次。如果最后能整个跳完第二个串,就 a n s + + ans++ ans++

然后我们可以通过 r m q rmq rmq n l o g n nlogn nlogn预处理之后, O ( 1 ) O(1) O(1)计算 l c p lcp lcp

感觉还是有一些细节需要看代码才行。

然后这个题就解决了!

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 4e5+1e2;
int wb[maxn],sa[maxn];
int rk[maxn],tmp[maxn];
int n,m;
int h[maxn],height[maxn];
char s[maxn],s1[maxn];
char a[maxn];
int len,len1;
int t;
int f[maxn][21];
void getsa()
{  int *x=rk,*y=tmp;
   int s=128;
   int p=0;
   for (int i=1;i<=n;i++) x[i]=a[i],y[i]=i;
   for (int i=1;i<=s;i++) wb[i]=0;
   for (int i=1;i<=n;i++) wb[x[y[i]]]++;
   for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
   for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
   for (int j=1;p<n;j<<=1)
   {  p=0;
   	  for (int i=n-j+1;i<=n;i++) y[++p]=i;
   	  for (int i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
   	  for (int i=1;i<=s;i++) wb[i]=0;
   	  for (int i=1;i<=n;i++) wb[x[y[i]]]++;
   	  for (int i=1;i<=s;i++) wb[i]+=wb[i-1];
   	  for (int i=n;i>=1;i--) sa[wb[x[y[i]]]--]=y[i];
   	  swap(x,y);
   	  p=1;
   	  x[sa[1]]=1;
   	  for (int i=2;i<=n;i++)
   	    x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+j]==y[sa[i-1]+j]) ? p : ++p;
   	  s=p;
   }
   for (int i=1;i<=n;i++) rk[sa[i]]=i;
   h[0]=0;
   for (int i=1;i<=n;i++)
   {
   	  h[i]=max(h[i-1]-1,0);
   	  while (i+h[i]<=n && sa[rk[i]-1]+h[i]<=n && a[i+h[i]]==a[sa[rk[i]-1]+h[i]]) h[i]++;
   } 
   for (int i=1;i<=n;i++) height[i]=h[sa[i]];
}
int query(int l,int r)
{
	int k = log2(r-l+1);
	return min(f[l][k],f[r-(1<<k)+1][k]);
}
void init()
{
    memset(wb,0,sizeof(wb));
    memset(sa,0,sizeof(sa));
    memset(rk,0,sizeof(rk));
    memset(h,0,sizeof(h));
    memset(height,0,sizeof(height));
    memset(tmp,0,sizeof(tmp));
    memset(f,127/3,sizeof(f));
}
int getlcp(int x,int y)
{
	if (rk[x]>rk[y]) swap(x,y); 
	return query(rk[x]+1,rk[y]);
}
bool check(int now)
{
    if(len-now+1<len1) return false;
    int rest=3;
    int rev=len+2;
    int x = getlcp(now,rev);
    rev+=x;
    now+=x;
    rest--;
    if(rev>n) return true;
    rev++;
    now++;
    if(rev>n) return true;
    x = getlcp(now,rev);
    rev+=x;
    now+=x;
    rest--;
    if(rev>n) return true;
    rev++;
    now++;
    if(rev>n) return true;
    x=getlcp(now,rev);
    rev+=x;
    now+=x;
    rest--;
    if(rev>n) return true;
    rev++;
    now++;
    if(rev>n) return true;
    x=getlcp(now,rev);
    rev+=x;
    if (rev<=n) return false;
    else return true;
} 
int main()
{
  cin>>t;
  while (t--)
  {
  	init();
    scanf("%s",s+1);
    len = strlen(s+1);
    scanf("%s",s1+1);
    len1 = strlen(s1+1);
    n=0;
    int ans=0;
    for (int i=1;i<=len;i++) a[++n]=s[i];
    a[++n]='*';
    for (int i=1;i<=len1;i++) a[++n]=s1[i];
    getsa();
    for (int i=1;i<=n;i++) f[i][0]=height[i];
    for (int j=1;j<=20;j++)
      for (int i=1;i<=n;i++)
         if(i+(1<<j)-1<=n)
         {
         	f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
         }
    for (int i=1;i<=len;i++)
      if(check(i)) ans++;
    cout<<ans<<"\n"; 
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值