hdoj 4644 BWT 字符串匹配

来源:互联网 发布:上海科创板交易软件 编辑:程序博客网 时间:2024/04/30 15:31

昨天比赛和强神讨论了快一个小时,也得出了题解上的方法,只是不知道该怎么实现,按照我们两个想的,要求出原串的时间复杂度是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
ca

cg

g$

ADD 3

g$a

caa

$ac

aac
aca

acg

cg$

SORT 3

$ac

aac
aca

acg

caa

cg$

g$a

ADD 4

g$ac

caac
$aca

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$

可以发现在给定字符串中的字符其前面有几个相同字符,排完序后仍然有几个相同的字符(后面本身就是按照字典序进行排列的,所以排序时只按照第一个字母的字典序就可以了),所以每次排序后每个串所在的位置就可以固定,由此在o(n)的时间求得原串、

接下来利用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 100096int 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");}}}


原创粉丝点击