• P5028 Annihilate


    (color{#0066ff}{ 题目描述 })

    黑暗之主的蜈蚣几乎可以毁灭一切,因此小正方形陷入了苦战……

    小正方形现在需要减弱黑暗之主的攻击。

    一个黑暗之主的攻击可以用一个仅有小写字母的字符串表示。

    现在黑暗之主向小正方形发动了若干攻击,对于两个攻击,小正方形能选出它们最长的公共子串,并把这一段消除。

    现在小正方形想要知道,对于任意两个黑暗之主的攻击,它们的最长公共子串长度是多少,你能帮帮它吗?

    (color{#0066ff}{输入格式})

    第一行为一个整数 (n),表示字符串数目。

    接下来 (n) 行,一行一个字符串,保证所有字符串长度之和 <= 1000000

    (color{#0066ff}{输出格式})

    输出共有(n) 行,每行 (n - 1)个正整数

    第一行第一个正整数表示第1个串与第2个串的最长公共子串

    第二个正整数表示第1个串与第3个串的最长公共子串

    ……

    第二行第一个正整数表示第2个串与第1个串的最长公共子串

    以此类推。

    (color{#0066ff}{输入样例})

    3
    abb
    bcc
    aba
    

    (color{#0066ff}{输出样例})

    1 2
    1 1
    2 1
    

    (color{#0066ff}{数据范围与提示})

    对于 (30\%) 的数据,(n leq 5),每个字符串长度 (leq 500)
    对于 (100\%) 的数据,(2leq nleq 50),字符串长度之和 (leq 1000000)
    注意:本题内存限制仅为 64 MB,请尽量使用内存运用优秀的方法。

    另外,对于占 60 Pts 的测试点,您每通过一个点即可获得 10 Pts

    对于剩下的测试点,您只有全部通过才能获得 40 Pts.

    对于所有数据点,不保证数据为随机生成。

    (color{#0066ff}{ 题解 })

    呜呜呜~~~~这题卡SAM!!!(64MB)

    于是写SA

    把所有字符串拼起来,每两个之间用完全不同的xjb字符连接

    然后跑SA

    要找最长公共字串,显然(O(n^2len))是过不了的qwq

    考虑(O(nlen))的方法

    我们要找的是最长的,又因为ans是一段h的min,所以肯定找最近的(贪心)

    记pre[i]为当前第i个字符串最近一次出现的位置

    然后每次用ST表查询区间min

    TM倪大野, 还尼玛卡我空间

    ST表TM会MLE7个点

    于是,pre[i]不再维护位置了,维护最近一次出现的位置到当前的(h_{min})

    这样每次更新一下,就行了

    恶心

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    LL in() {
    	char ch; int x = 0, f = 1;
    	while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    	return x * f;
    }
    const int maxn = 1e6 + 52;
    const int inf = 0x7f7f7f7f;
    int rk[maxn], x[maxn], y[maxn], c[maxn], sa[maxn], len[55], s[maxn], ht[maxn];
    int n, m, t;
    void SA() {
    	m = 600, t = len[n] + 1;
    	for(int i = 1; i <= t; i++) c[x[i] = s[i]]++;
    	for(int i = 1; i <= m; i++) c[i] += c[i - 1];
    	for(int i = t; i >= 1; i--) sa[c[x[i]]--] = i;
    	for(int k = 1; k <= t; k <<= 1) {
    		int num = 0;
    		for(int i = t - k + 1; i <= t; i++) y[++num] = i;
    		for(int i = 1; i <= t; i++) if(sa[i] > k) y[++num] = sa[i] - k;
    		for(int i = 1; i <= m; i++) c[i] = 0;
    		for(int i = 1; i <= t; i++) c[x[i]]++;
    		for(int i = 1; i <= m; i++) c[i] += c[i - 1];
    		for(int i = t; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
    		std::swap(x, y);
    		x[sa[1]] = 1, num = 1;
    		for(int i = 2; i <= t; i++) 
    			x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k])? num : ++num;
    		if(num == t) break;
    		m = num;
    	}
    	for(int i = 1; i <= t; i++) rk[i] = x[i];
    	int h = 0;
    	for(int i = 1; i <= t; i++) {
    		if(rk[i] == 1) continue;
    		if(h) h--;
    		int j = sa[rk[i] - 1];
    		while(j + h <= t && s[i + h] == s[j + h]) h++;
    		ht[rk[i]] = h;
    	}
    }
    int ans[55][55];
    int bel[maxn];
    int pre[55];
    int main() {
    	n = in();
    	len[0] = -1;
    	for(int i = 1; i <= n; i++) {
    		char ch;
    		while(!isalpha(ch = getchar()));
    		for(len[i] = len [i - 1] + 1, s[++len[i]] = ch, bel[len[i]] = i; isalpha(ch = getchar()); s[++len[i]] = ch, bel[len[i]] = i);
    		s[len[i] + 1] = 300 + i;
    	}
    	SA();
    	for(int i = 2; i <= t; i++) {
    		for(int j = 1; j <= n; j++) pre[j] = std::min(pre[j], ht[i]);
    		int b = bel[sa[i]];
    		pre[bel[sa[i - 1]]] = ht[i];
    		if(!b) continue;
    		for(int j = 1; j <= n; j++) ans[j][b] = ans[b][j] = std::max(ans[b][j], pre[j]);
    	}
    	for(int i = 1; i <= n; i++) {
    		for(int j = 1; j <= n; j++) {
    			if(i == j) continue;
    			printf("%d ", ans[i][j]);
    		}
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Unix + OS IBM Aix System Director
    framework apache commons
    维吉尼亚密码
    破译换字式密码
    维吉尼亚密码表
    维吉尼亚密码表
    delete i;
    破译换字式密码
    破译换字式密码
    int *i = new int;
  • 原文地址:https://www.cnblogs.com/olinr/p/10254835.html
Copyright © 2020-2023  润新知