【题解】Codeforces 427D. Match & Catch 后缀自动机

给定两个字符串A,B(5000),找到最短的唯一公共子串,无解输出-1.

唯一公共子串是指在A和B中均只出现了一次。


A#B建立后缀自动机,维护endpos集的三个属性:出现次数cnt、第一次出现位置firstpos、最后一次出现位置lastpos.

遍历所有节点,如果cnt=2且 f i r s t p o s < # firstpos<\#所在位置 l a s t p o s > # lastpos>\#所在位置 ,那么这个节点对应的最短字符串 ( l e n [ l i n k [ i ] ] + 1 ) (len[link[i]]+1) 是一个可行的答案。

注意一点,出现次数为2的字符串,一定不会是含有#号的。


本来不想写题解的,但是这道题几乎把能犯的错误全犯了,特此总结一下:

  1. 交错题
  2. 字符集映射错,要添加 # \# 号,但是忘了把字符集扩展到128.
  3. 01下标起始搞乱,设置pos的时候按1下标,算的时候又按0下标,就错了.
  4. lastpos数组没有开两倍空间.

状态好差啊,睡会再写。

string suffix structrues一共56道题(截至2019-11-13),现在已经做了15道,加油。

/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 10016, MOD = 1000000007;


int sz, lst; //后缀自动机大小,上一次插入的节点
int ch[M<<1][128], len[M<<1], link[M<<1], cnt[M<<1];
int firstpos[M<<1], lastpos[M<<1]; 
void extend(const char *s)
{
	for(int i=0; s[i]; ++i)
	{
		int c = s[i];
		int cur = ++sz;
		len[cur] = len[lst] + 1;

		int p = lst;
		while(!ch[p][c])
		{
			ch[p][c] = cur;
			p = link[p];
		}
		if(ch[p][c] != cur)
		{
			int q = ch[p][c];
			if(len[p] + 1 == len[q]) link[cur] = q;
			else
			{
				int clone = ++sz;
				memcpy(ch[clone], ch[q], sizeof(ch[q]));
				link[clone] = link[q];
				firstpos[clone] = firstpos[q];
				lastpos[clone] = lastpos[q];
				len[clone] = len[p]+1;
				while(ch[p][c]==q)
				{
					ch[p][c] = clone;
					p = link[p];
				}
				link[q] = link[cur] = clone;
			}
		}
		lst = cur;
		++cnt[cur]; 
		firstpos[cur] = lastpos[cur] = i;
	}

	vector<int> nodes;
	for(int i=1; i<=sz; ++i)
		nodes.push_back(i);
	sort(nodes.begin(), nodes.end(), [&](int a, int b){
		return len[a]>len[b];
	});
	for(auto u:nodes)
	{
		firstpos[link[u]] = min(firstpos[link[u]], firstpos[u]);
		lastpos[link[u]] = max(lastpos[link[u]], lastpos[u]);
		cnt[link[u]] += cnt[u];
	}
}

char tex[M];
int main(void)
{
	#ifdef _LITTLEFALL_
	freopen("in.txt","r",stdin);
    #endif

	scanf("%s", tex); int n = strlen(tex);
	tex[n] = '#';
	scanf("%s", tex+n+1);
	extend(tex);

	int ans = -1;
	for(int i=1; i<=sz; ++i)
	{
		// printf("i=%d cnt=%d first=%d last=%d\n",i,cnt[i],firstpos[i],lastpos[i] );
		// printf("len=%d milen=%d\n",len[i],len[link[i]]+1 );
		//出现次数为2的,一定不可能是含#的串.
		if(cnt[i]==2 && firstpos[i]<n && lastpos[i]>n)
		{
			if(ans==-1 || len[link[i]]+1<ans)
				ans = len[link[i]]+1;
		}
	}
	printf("%d\n",ans );

    return 0;
}


inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
发布了375 篇原创文章 · 获赞 305 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/m0_37809890/article/details/103049593
今日推荐