Longest Common Substring(最長公共子串)

SP1811

題目描述
A string is finite sequence of characters over a non-empty finite set Σ.
In this problem, Σ is the set of lowercase letters.
Substring, also called factor, is a consecutive sequence of characters occurrences at least once in a string.
Now your task is simple, for two given strings, find the length of the longest common substring of them.
Here common substring means a substring of two or more strings.
輸入格式
The input contains exactly two lines, each line consists of no more than 250000 lowercase letters, representing a string.
輸出格式
The length of the longest common substring. If such string doesn’t exist, print “0” instead.
題意翻譯
輸入2 個長度不大於250000的字符串,輸出這2 個字符串的最長公共子串。如果沒有公共子串則輸出0 。
Translated by @xyz32768
輸入輸出樣例
輸入
alsdfkjfjkdsal
fdjskalajfkdsla
輸出
3

有兩個字符串AABB,我們要求它們的最長公共連續子串。
首先,我們對AA建立一個SAMSAM
定義LenLen爲當前BB的前ii個字符組成的子串與AA的最長公共子序列,StateState爲當前狀態,初始化爲0(初始狀態)。next(State,ch)next(State,ch)爲在StateState狀態節點處往chch道路走的下一狀態。
匹配
然後我們從B[i=0]B[i=0]開始,在AASAMSAM上走,一個一個匹配,若:

  1. 當前狀態朝着B[i]B[i]往下走有路,說明可以繼續往下匹配,就接着走,即State=next(State,B[i])++LenState=next(State,B[i]),++Len
  2. 如果沒有路了,就跳到當前狀態在後綴連接樹上的父節點,如果父節點還是沒有B[i]B[i]的路,就一直往上跳(即State=link(State)State=link(State)),直到遇到能往下走的邊。此時就令Len=len()+1Len=len(當前狀態)+1State=next(State,B[i])State=next(State,B[i])
  3. 如果跳到頭了都沒有能走的路的話,就說明要從B[i]開始重新匹配,令Len=0State=0Len=0,State=0

原理:如果B[i]B[i]在當前位置下失配(無路可走),那麼說明當前狀態下的所有子串都失配了,但是它的後綴連接樹上的父節點不一定失配,就繼續往上找,即相當於當前已經匹配的AA的子串的左邊界往右移,然後繼續找路。如果一直沒路,就一直往上找,直到達到 初始狀態 ,如果此時仍沒有路的話,說名在當前LenLen長度下已經是B[i]B[上一次從初始狀態開始匹配的i]開始的最長公共子串了,無法在加長了。那就讓Ans=max(Ans,Len)Ans=max(Ans,Len),讓後以B[i]B[i]爲新的開頭從初始狀態重新開始匹配。即整個過程就是再找BB的所有前綴的後綴最長能和AA匹配多少。

#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
using namespace std;
const int MAXN = 250005;
int n;
char
A[MAXN],
B[MAXN];
struct SAM {
	int size, last;
	struct Node {
		int len = 0, link = 0;
		int next[26];
		void clear() {
			len = link = 0;
			memset(next, 0, sizeof(next));
		}
	} node[MAXN * 2];
	void init() {
		for (int i = 0; i < size; i++) {
			node[i].clear();
		}
		node[0].link = -1;
		size = 1;
		last = 0;
	}
	void insert(char x) {
		int ch = x - 'a';
		int cur = size++;
		node[cur].len = node[last].len + 1;
		int p = last;
		while (p != -1 && !node[p].next[ch]) {
			node[p].next[ch] = cur;
			p = node[p].link;
		}
		if (p == -1) {
			node[cur].link = 0;
		}
		else {
			int q = node[p].next[ch];
			if (node[p].len + 1 == node[q].len) {
				node[cur].link = q;
			}
			else {
				int clone = size++;
				node[clone] = node[q];
				node[clone].len = node[p].len + 1;
				while (p != -1 && node[p].next[ch] == q) {
					node[p].next[ch] = clone;
					p = node[p].link;
				}
				node[q].link = node[cur].link = clone;
			}
		}
		last = cur;
	}
}sam;
int getNextState(const int& CurState,int Loc) {
	return sam.node[CurState].next[Loc - 'a'];
}
int Compute(int n) {
	int
		&& Ans = 0,
		&& CurState = 0,
		&& Len = 0;
	for (int i = 0; i < n; ++i) {
		//如果有路可走,就走噻
		if (getNextState(CurState, B[i])) {
			CurState = getNextState(CurState, B[i]);
			++Len;
		}
		//否則
		else {
			//跳link
			for (CurState = sam.node[CurState].link;; CurState = sam.node[CurState].link) {
				//如果跳到了
				if (CurState > 0 && getNextState(CurState, B[i])) {
					Len = sam.node[CurState].len + 1;
					CurState = getNextState(CurState, B[i]);
					break;
				}
				//如果跳到初始狀態。
				else if (CurState <= 0) {
					Len = 0;
					CurState = 0;
					break;
				}
			}
		}
		Ans = max(Ans, Len);
	}
	return Ans;
}
int main() {
	scanf("%s%s", &A, &B);
	int Len_A = strlen(A);
	sam.init();
	for (int i = 0; i < Len_A; ++i) {
		sam.insert(A[i]);
	}
	printf("%d", Compute(strlen(B)));
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章