hdoj 4644 BWT 字符串匹配

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淡定的小Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值