ZOJ 3228 Searching the String

题目

简明题意:给你一个串,之后给出若干询问串,询问每个询问串在原串中(在是否可重叠的情况下)出现了几次.

这道题有两种解法:

  1. AC自动机,对讯问离线.
  2. 构造SAM,在线求(太久没打,原理模板都忘光了~)

这两者有何区别呢?
其实很简单,一个是对模式串(可理解为较短的串)构造数据结构,另一个是对匹配串(可理解为长串)进行构造数据结构.

一般SAM的常数会大得多,而且不太好打.

步入正题

很明显,对于两个参数(是否可以重叠,字符串)都相同的询问的答案相同.
我们可以把任务放到Trie树上求,最后定位输出即可.(本来傻傻用vector存对应位置,T^T)
对于0操作,就不赘述.
而对于1操作,只需要贪心统计最后一个位置在哪,然后判断放不放的下即可.

代码简洁易懂:

#include
#include
#include
#include
using namespace std;
const int N=1e5+10,T=3.2e5+10;

void qw(int x) {
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
void pr2(int x) {qw(x); puts("");}

int trie[T][26],fail[T],dep[T],nxt[T],f[T][3],tot; 
char ed[T];//描述任务 

char s[N][8],str[N];
int type[N],id[N];//每个模式串的类型,在Trie树上的标号 
int n,m;

int ins(char *s,int type,int id) {
	int p=0;
	for(int i=0;s[i];i++) {
		int c=s[i]-'a';
		if(!trie[p][c]) trie[p][c]=++tot;
		p=trie[p][c];
	}
	ed[p]|=1<<type; return p;
}

int q[T],l,r;
void bfs() {
	q[l=r=1]=0;
	while(l<=r) {
		int p=q[l++];
		for(int c=0,x;c<26;c++) {
			if(!(x=trie[p][c])) {trie[p][c]=trie[fail[p]][c]; continue;}
			dep[x]=dep[p]+1;
			if(p) {	
				fail[x]=trie[fail[p]][c];
				nxt[x]=(ed[fail[x]]?fail[x]:nxt[fail[x]]);
			}
			else fail[x]=nxt[x]=0;
			q[++r]=x;
		}
	}
}

void clear() {
	memset(trie,0,(tot+1)*26<<2);
	memset(ed,0,tot+1);
	tot=0;
}

void solve() {
	static int num=0;
	n=strlen(str+1);
	for(int i=1,p=0,q;i<=n;i++) {
		int c=str[i]-'a';
		p=trie[p][c];
		q=p;
		while(q) {
			f[q][0]+=ed[q]&1;
			if((ed[q]&2)&&i-f[q][2]>=dep[q])
				f[q][1]++,f[q][2]=i; 
			q=nxt[q];
		}
	}
	printf("Case %d\n",++num);
	for(int i=1;i<=m;i++) pr2(f[id[i]][type[i]]);
	for(int i=1;i<=m;i++) f[id[i]][0]=f[id[i]][1]=f[id[i]][2]=0;
	puts("");
}

int main() {
	while(~scanf("%s %d",str+1,&m)) {
		clear();
		for(int i=1;i<=m;i++) {
			scanf("%d %s",&type[i],s[i]);
			id[i]=ins(s[i],type[i],i);
		}
		bfs(); solve();
	}
	return 0;
}

你可能感兴趣的:(字符串)