昨天比赛和强神讨论了快一个小时,也得出了题解上的方法,只是不知道该怎么实现,按照我们两个想的,要求出原串的时间复杂度是o(n^2*logn),而且空间也很大,没办法写,然后今天看了大牛的标程,发现分析还是差了那么一点。
大牛的解析:
可以注意到生成的BWT(T)可以还原成为原始的T,方法如下表所示
ADD 1 g c $ a a a c | SORT 1 $ a a a c c g | ADD 2 g$ ca $a aa ac ac cg | SORT 2 $a aa ac ac cg g$ |
ADD 3 g$a caa $ac aac acg cg$ | SORT 3 $ac aac acg caa cg$ g$a | ADD 4 g$ac caac aacg acaa acg$ cg$a | SORT 4 $aca aacg acaa acg$ caac cg$a g$ac |
ADD 5 g$aca caacg $acaa aacg$ acaac acg$a cg$ac | SORT 5 $acaa aacg$ acaac acg$a caacg cg$ac g$aca | ADD 6 g$acaa caacg$ $acaac aacg$a acaacg acg$ac cg$aca | SORT 6 $acaac aacg$a acaacg acg$ac caacg$ cg$aca g$acaa |
ADD 7 g$acaac caacg$a $acaacg aacg$ac acaacg$ acg$aca cg$acaa | SORT 7 $acaacg aacg$ac acaacg$ acg$aca caacg$a cg$acaa g$acaac |
|
|
注意分析就会发现,每次对新增加字符的字符串进行排序时,其排序的位置都是相同的,即例子中的原串为gc$aaac,第一次按照升序排列好为$aaaccg,将两个串合并后的到的所有的字符串就是
g$ 排完序后为 $a
ca aa
$a ac
aa ac
ac ca
ac cg
cg g$
接下来利用kmp算法依次来匹配下面的询问就可以了。(我是多久没有写过kmp啦,竟然把next数组建立在了主串上,贡献了好多的TLE O_O );
下面的代码是看了标程之后写的,不懂可以看注释!
/
// File Name: 4644.cpp
// Author: wang
// mail:
// Created Time: 2013-8-7 10:39:29
/
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <cstring>
#include <cmath>
#include<string>
#include <algorithm>
#include<iostream>
#include<queue>
#include <map>
using namespace std;
typedef long long ll;
#define INF (INT_MAX/10)
#define SQR(x) ((x)*(x))
#define rep(i, n) for (int i=0; i<(n); ++i)
#define repf(i, a, b) for (int i=(a); i<=(b); ++i)
#define repd(i, a, b) for (int i=(a); i>=(b); --i)
#define clr(ar,val) memset(ar, val, sizeof(ar))
#define pb(x) push_back(x)
#define N 100096
int n;
int next[N];
char a[200010];
string s;
string solve()
{
string but=s;
vector<int>cont(s.length(),0);//记录最后一行的字符前面字符的个数的
vector<int>pos[30];
//用来记录给定的字符串中第i个字符其前面与它相同的字符的个数的
rep(i,but.size())
if(but[i]!='$')
{
cont[i]=pos[but[i]-'a'].size();//前面相同字符的个数的
pos[but[i]-'a'].pb(i);
}
//对给定的字符进行排序,来保证最先的字典序的
string sorted=s;
sort(sorted.begin(),sorted.end());
// cout<<sorted<<endl;
//排序后第i出现的某个字符所在的位置的,即指向添加后再排序所在的位置的
rep(i,26) pos[i].clear();
repf(i,1,sorted.length()-1)
pos[sorted[i]-'a'].pb(i);
//进行操作的核心
string dis;
char ch=s[0];//结尾的第一个字符的
int place=0;//结尾的第一个字符排序后所在相同字符的位置的,因为后面为'$',所以肯定是第一个的
rep(i,s.length()-1)
{
dis+=ch;//该字符添加后排序所对应的位置的
int pp=pos[ch-'a'][place];//其后前面再次添加的字符即为原串所对应的字符
ch=s[pp];//获得新前面的字符
place=cont[pp];//新获得的字符排序后所在相同字符中的位置的
}
reverse(dis.begin(),dis.end());
return dis;
}
void kmp(string dis)
{
memset(next,-1,sizeof(next));
int j=-1;
rep(i,dis.length())
{
while(j!=-1 && dis[i]!=dis[j]) j=next[j];
next[i+1]=++j;
}
next[0]=0;
}
int main()
{
while(cin>>s)
{
string p=solve();
int q;
scanf("%d",&q);
while(q--)
{
scanf("%s",a);
int len=strlen(a);
string pp=a;
kmp(pp);
int j=0;
int flag=0;
rep(i,p.length())//匹配
{
while(a[j]!=p[i] && j!=0) j=next[j];
if(a[j]==p[i]) ++j;
if(j==len)
{
flag=1; break;
}
}
if(flag==1) printf("YES\n");
else printf("NO\n");
}
}
}