(http://www.elijahqi.win/2017/07/21/tjoi-2017dna-%E3%80%90bzoj4892luogu3763%E3%80%91/)
题目描述
加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状。现在研究人员想知道这个基因在DNA链S0上的位置。所以你需要统计在一个表现出吃藕性状的人的DNA序列S0上,有多少个连续子串可能是该基因,即有多少个S0的连续子串修改小于等于三个字母能够变成S。
输入输出格式
输入格式:
第一行有一个数T,表示有几组数据 每组数据第一行一个长度不超过10^5的碱基序列S0
每组数据第二行一个长度不超过10^5的吃藕基因序列S
输出格式:
共T行,第i行表示第i组数据中,在S0中有多少个与S等长的连续子串可能是表现吃藕性状的碱基序列
输入输出样例
输入样例#1:
1
ATCGCCCTA
CTTCA
输出样例#1:
2
说明
对于20%的数据,S0,S的长度不超过10^4
对于20%的数据,S0,S的长度不超过10^5,0< T<=10
天津今年的省选题很有质量啊,测试数据也非常好,不像往年写写暴力就能过不少,于是 今年就差点爆0了 ORZ
一道后缀数组加暴力的题 期望复杂度在nlogn+n吧 可能会算错,不太会算(。・∀・)ノ希望指正
我自己在bzoj上似乎被卡常了,因为用的是何琦的sa??黑人问号,省选的时候lzh学长当场写过,看了看他的程序似乎是用了坊间传说的da算法。我们省选是开o2优化的,于是在洛谷上 大牛模式(有o2优化) 我就交过了
程序挺长的 大概因为我一开始总是过不去加的调试太多 前面rmq+sa求lcp可以看下我之前的文章
SA:http://www.elijahqi.win/2017/07/12/poj-2774/
RMQ:http://www.elijahqi.win/2017/07/13/%e3%80%90luogu2880%e3%80%91usaco07jan%e5%b9%b3%e8%a1%a1%e7%9a%84%e9%98%b5%e5%ae%b9balanced-lineup/
SA+RMQ求lcp:
http://www.elijahqi.win/2017/07/15/ural1297/
大概说一下暴力部分如何去求,由于说我们可以nlogn求lcp然后o1的时间查询
我们枚举原串的每一个字符作为起点,如果相同就用lcp求,不相同就给记录不相同的计数器++
保证不相同的计数器不超过3 如果在3以内我们就统计答案
#include
#include
#include
#define N 220000
int T,n,n1,m,count[N],tmp[N],rank[N<<1],rank1[N],sa[N],height[N],k,fmin1[N][18];
char a[N];
inline void swap(int &a,int &b){
int t=a;a=b;b=t;
}
inline int min(int x,int y){
return x<y?x:y;
}
inline int lcp(int l,int r){
int a=rank[l],b=rank[r];
if (a>b) swap(a,b);a++;
int k=0;k=(int)log2(b-a+1);//while ((1<<(k+1))<=(b-a+1))++k;
return min(fmin1[a][k],fmin1[b-(1<1][k]);
}
int main(){
//freopen("3763.in","r",stdin);
// freopen("3763.out","w",stdout);
scanf("%d",&T);
while (T--){
/* char ch=getchar();n1=0;
while (ch<'A'||ch>'Z') ch=getchar();
while (ch<='Z'&&ch>='A') {
a[++n1]=ch;ch=getchar();
}*/
scanf("%s",a+1);n1=strlen(a+1);
a[++n1]='#';
scanf("%s",a+n1+1);
n=strlen(a+1);
/* n=n1;
while (ch<'A'||ch>'Z') ch=getchar();
while (ch<='Z'&&ch>='A') {
a[++n]=ch;ch=getchar();
}
n=strlen(a+1);*/
//n1=n-n1;
m=5;
//printf("%d ",n1);
// for (int i=1;i<=n;++i) printf("%c",a[i]);
for (int i=1;i<=255;++i) count[i]=0;
memset(rank,0,sizeof(rank));
for (int i=1;i<=n;++i) count[a[i]]=1;
for (int i=1;i<=255;++i) count[i]+=count[i-1];
for (int i=1;i<=n;++i) rank[i]=count[a[i]];
k=0;
for (int p=1;k!=n;p<<=1,m=k){
for (int i=1;i<=m;++i) count[i]=0;
for (int i=1;i<=n;++i) count[rank[i+p]]++;
for (int i=1;i<=m;++i) count[i]+=count[i-1];
for (int i=n;i>=1;--i) tmp[count[rank[i+p]]--]=i;
for (int i=1;i<=m;++i) count[i]=0;
for (int i=1;i<=n;++i) count[rank[i]]++;
for (int i=1;i<=m;++i) count[i]+=count[i-1];
for (int i=n;i>=1;--i) sa[count[rank[tmp[i]]]--]=tmp[i];
memcpy(rank1,rank,sizeof(rank)>>1);
rank[sa[1]]=k=1;
for (int i=2;i<=n;++i){
if (rank1[sa[i-1]]!=rank1[sa[i]]||rank1[sa[i-1]+p]!=rank1[sa[i]+p])++k;
rank[sa[i]]=k;
}
}
/* for (int i=1;i<=n;++i){
for (int j=sa[i];j<=n;++j) printf("%c",a[j]);printf("\n");
}*/
k=0;
for (int i=1;i<=n;++i){
if (rank[i]==0){
height[1]=0;continue;
}
k=k==0?0:k-1;
while (a[i+k]==a[sa[rank[i]-1]+k])++k;
height[rank[i]]=k;
}
//for (int i=1;i<=n;++i) printf("%d ",height[i]);
// memset(fmin1,0x7f,sizeof(fmin1));
for (int i=1;i<=n;++i) fmin1[i][0]=height[i];
for (int j=1;(1<for (int i=1;i+(1<1<=n;++i){
fmin1[i][j]=min(fmin1[i][j-1],fmin1[i+(1<<(j-1))][j-1]);
//printf("%d ",fmin[i][j]);
}
// printf("\n");
}
/* int d=n1;int ans=0;
for (int i=1;i<=n;++i){
if (height[i]>=n1-3){
if ((sa[i]/d)^(sa[i-1]/d)) ans++;
}
}*/
int na=n1,nb=n-n1,ans=0;
for (int i=1;i<=na-nb;i++){
int tot=0;
for (int j=1;j<=nb&&tot<=3;)
if (a[i+j-1]!=a[na+j]) {
tot++,j++;if (tot>3) break;
}
else j+=lcp(i+j-1,na+j);
if (tot<=3) ans++;
// if (tot>3) continue;
}
printf("%d\n",ans);
}
return 0;
}