SPOJ LCS (后缀自动机)

SPOJ LCS

Problem : 给两个串S、T,询问两个串的最长公共子串。
Solution :复(yu)习(xi)了一下后缀自动机。后缀自动机一定要结合后缀树来理解呀 !!!后缀自动机的fail指针实现了原串的逆序的后缀树,后缀自动机的nt转移数组相当于后缀树上的后缀链。

#include <iostream>
#include <string>

using namespace std;

const int N = 250008;

struct Suffix_Automaton
{
	int nt[N << 1][26], fail[N << 1], a[N << 1];
	int p, q, np, nq;
	int tot, last, root;
	int newnode()
	{
		for (int i = 0; i < 26; ++i) nt[tot][i] = -1;
		fail[tot] = -1; a[tot] = 0;
		return tot++;
	}
	void clear()
	{
		tot = last = 0; 
		root = newnode();
	}
	void insert(int ch)
	{
		p = last; last = np = newnode(); a[np] = a[p] + 1;
		for (; ~p && nt[p][ch] == -1; p = fail[p]) nt[p][ch] = np;
		if (p == -1) fail[np] = root;
		else
		{
			q = nt[p][ch];
			if (a[q] == a[p] + 1) fail[np] = q;
			else
			{
				nq = newnode();
				for (int i = 0; i < 26; ++i) nt[nq][i] = nt[q][i];  //i不要打成ch
				fail[nq] = fail[q]; fail[q] = fail[np] = nq;   //第一句 fail[q] 不一定等于p, nq是复制q的出边
				a[nq] = a[p] + 1;
				for (; ~p && nt[p][ch] == q; p = fail[p]) nt[p][ch] = nq;
			}
		}
	}
	void solve(const string &s)
	{
		int p = root, cnt = 0, ans = 0;
		for (int i = 0, len = s.length(); i < len; ++i)
		{
			int ch = s[i] - 'a';
			if (~nt[p][ch]) p = nt[p][ch], cnt++;
			else
			{
				for(; ~p && nt[p][ch] == -1; p = fail[p]);
				if (p == -1) p = root, cnt = 0;
				else cnt = a[p] + 1, p = nt[p][ch];
			}
			ans = max(ans, cnt);
		}
		cout << ans << endl;
	}
}sam;

int main()
{
	cin.sync_with_stdio(0);
	string s, t;
	while (cin >> s >> t)
	{
		sam.clear();
		for (int i = 0, len = s.length(); i < len; ++i)
			sam.insert(s[i] - 'a');
		sam.solve(t);
	}
}

posted @ 2017-07-21 12:14  rpSebastian  阅读(206)  评论(0编辑  收藏  举报