• [SPOJ1812]Longest Common Substring II


    [SPOJ1812]Longest Common Substring II

    试题描述

    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 a bit harder, for some 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 at most 10 lines, each line consists of no more than 100000 lowercase letters, representing a string.

    输出

    The length of the longest common substring. If such string doesn't exist, print "0" instead.

    输入示例

    alsdfkjfjkdsal
    fdjskalajfkdsla
    aaaajfaaaa

    输出示例

    2

    数据规模及约定

    见“输入

    题解

    考虑把所有串连接起来构建后缀自动机(之前一看到这种题果断想后缀数组),那么我们在 parent 树上处理出每个节点子树中包含的串的集合。那么最后扫一遍每个节点,对于所有满的集合用 Max[i] 更新答案就好了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
     
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
     
    #define maxn 2000110
    #define maxa 40
     
    int rt, ToT, last, ch[maxn][maxa], par[maxn], Max[maxn], id[maxn];
     
    char Str[maxn];
    int S[maxn], Id[maxn], len;
     
    void extend(int i) {
    	int x = S[i], p = last, np = ++ToT; Max[np] = Max[p] + 1; id[np] = (Id[i] <= 10 ? (1 << Id[i]) : 0);
    	last = np;
    	while(p && !ch[p][x]) ch[p][x] = np, p = par[p];
    	if(!p){ par[np] = rt; return ; }
    	int q = ch[p][x];
    	if(Max[q] == Max[p] + 1){ par[np] = q; return ; }
    	int nq = ++ToT; Max[nq] = Max[p] + 1;
    	memcpy(ch[nq], ch[q], sizeof(ch[q]));
    	par[nq] = par[q];
    	par[np] = par[q] = nq;
    	while(p && ch[p][x] == q) ch[p][x] = nq, p = par[p];
    	return ;
    }
     
    int sa[maxn], Ws[maxn];
    void build() {
    	for(int i = 1; i <= ToT; i++) Ws[len-Max[i]]++;
    	for(int i = 1; i <= len; i++) Ws[i] += Ws[i-1];
    	for(int i = ToT; i; i--) sa[Ws[len-Max[i]]--] = i;
    	for(int i = 1; i <= ToT; i++) id[par[sa[i]]] |= id[sa[i]];
    	return ;
    }
     
    int main() {
    	rt = last = ToT = 1;
    	int cnt = 0;
    	while(~scanf("%s", Str)) {
    		int n = strlen(Str);
    		for(int i = 0; i < n; i++) S[++len] = Str[i] - 'a', Id[len] = cnt;
    		S[++len] = cnt + 26; Id[len] = cnt + 11;
    		cnt++;
    	}
    	
    	for(int i = 1; i <= len; i++) extend(i);
    	build();
    	int all = (1 << cnt) - 1, ans = 0;
    	for(int i = 1; i <= ToT; i++)
    		if(id[i] == all) ans = max(ans, Max[i]);
    	
    	printf("%d
    ", ans);
    	
    	return 0;
    }
    

    然而 SPOJ 卡常数 T 飞~~~

    考虑优化,因为这个最长公共子串一定出现在某一个字符串中,所以我们只需要对第一个串构造 SAM,对于后面的串,我们分别扫一遍在 SAM 上转移,然后再每个节点上更新一下当前匹配的最长长度信息,然后取取 min max 什么的就好了。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    int read() {
    	int x = 0, f = 1; char c = getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
    	return x * f;
    }
    
    #define maxn 200010
    #define maxa 26
    
    int rt, ToT, last, ch[maxn][maxa], par[maxn], Max[maxn], Ans[maxn], mn[maxn];
    
    char Str[maxn];
    int len;
    
    void extend(int i) {
    	int x = Str[i] - 'a', p = last, np = ++ToT; Max[np] = Max[p] + 1;
    	last = np;
    	while(p && !ch[p][x]) ch[p][x] = np, p = par[p];
    	if(!p){ par[np] = rt; return ; }
    	int q = ch[p][x];
    	if(Max[q] == Max[p] + 1){ par[np] = q; return ; }
    	int nq = ++ToT; Max[nq] = Max[p] + 1;
    	memcpy(ch[nq], ch[q], sizeof(ch[q]));
    	par[nq] = par[q];
    	par[np] = par[q] = nq;
    	while(p && ch[p][x] == q) ch[p][x] = nq, p = par[p];
    	return ;
    }
    int sa[maxn], Ws[maxn];
    void build() {
    	for(int i = 1; i <= ToT; i++) Ws[len-Max[i]]++;
    	for(int i = 1; i <= len; i++) Ws[i] += Ws[i-1];
    	for(int i = ToT; i; i--) sa[Ws[len-Max[i]]--] = i;
    	return ;
    }
    void get(int n) {
    	int p = rt, tmp = 0;
    	memset(Ans, 0, sizeof(Ans));
    	for(int i = 0; i < n; i++) {
    		int x = Str[i] - 'a';
    		if(ch[p][x]) p = ch[p][x], tmp++;
    		else {
    			while(p && !ch[p][x]) p = par[p];
    			if(!p) tmp = 0, p = 1;
    			else tmp = Max[p] + 1, p = ch[p][x];
    		}
    		Ans[p] = max(Ans[p], tmp);
    	}
    	for(int i = 1; i <= ToT; i++) {
    		int u = sa[i];
    		mn[u] = min(mn[u], Ans[u]);
    		if(par[u] && Ans[u]) Ans[par[u]] = Max[par[u]];
    	}
    	return ;
    }
    
    int main() {
    	rt = last = ToT = 1;
    	scanf("%s", Str); len = strlen(Str);
    	for(int i = 0; i < len; i++) extend(i);
    	build();
    	for(int i = 1; i <= ToT; i++) mn[i] = Max[i];
    	while(~scanf("%s", Str)) get(strlen(Str));
    	
    	int ans = 0;
    	for(int i = 1; i <= ToT; i++) ans = max(ans, mn[i]);
    	printf("%d
    ", ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    sys.stdout.flush-倒计时
    wget 网站扒取
    万能英数脚本
    sample function
    get_time
    读取指定行
    request设置cookies
    resize2fs
    闭包与认识装饰器
    函数的名称空间与作用域
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/6544327.html
Copyright © 2020-2023  润新知