1. 程式人生 > >洛谷3763 TJOI2017 DNA (SA+RMQ)

洛谷3763 TJOI2017 DNA (SA+RMQ)

題目連結

首先一看到這個題,由於是兩個串,所以一定是第一步把兩個串拼起來,然後中間加入一個非法字元。

然後我們考慮怎麼算這個東西?

由於存在一個可以修改三次的設定。
那我們不妨直接先跑一遍 S A SA ,然後求出來各個陣列。

直接列舉原串中的每一個字尾,然後暴力和後面的匹配。
令一個指標指到當前列舉的字尾,另一個指到第二串的開頭,每次可以跳 l

c p lcp 的長度,遇到一個不同的,就強行跳過。一共可以強行跳過三次。如果最後能整個跳完第二個串,就 a n s + +
ans++

然後我們可以通過 r m q rmq n l

o g n nlogn 預處理之後, O ( 1 ) O(1) 計算 l c p 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;
}