2020.11.9

(mathcal{A})
后缀数组

求法一般有(O(nlogn))的倍增,(O(n))的SA-IS,还有一个(O(nlog^2n))的hash。其中hash可以动态用set维护。

(ht[i])表示排名为(i)的后缀和排名为(i-1)的后缀的最长公共前缀长度。

  1. 单个字符串的相关问题的一个常用做法是先求后缀数组和 height数组,然后利用 height数组进行求解。

  2. 利用 height值对后缀进行分组的方法很常用,请读者认真体会。

给定一个字符串(s)
  • 求本质不同的子串个数。

    对于(sa[i]),假设排名前(i-1)个有(t)个本质不同的字串,以(sa[i])开始的共有(n - sa[i] + 1)个,但其中有(ht[i])个和(sa[i - 1])重复,所有排名前(i)个共有(t+n-sa[i]+1-ht[i])个,归纳得有(frac{n(n+1)}{2}-sum ht[i])

  • 求最长的两个相同子串,要求不相交。

    二分答案。把(ht[i]geq mid)的分成一组。就是把(ht)数组划分成若干段。每段间(ht[i]geq mid)

    发现只有同一组之内的任意两个字串都满足(geq k)的要求,而不同之见的肯定不满足。

    如果要不重复,只需(sa[i]-sa[j]>=k)。时间复杂度(O(nlogn))

  • (s)所有循环同构的字符串的顺序。([JSOI2007]字符加密)

    从循环同构可以想到令(s'=ss)(s')(sa)数组。所有开始位置(leq n)的即为所有循环同构的字符串。

  • 求每个(s[1..i])的本质不同的子串个数。([SDOI2016]生成魔咒)

    相当于每次增加一个字符,动态维护(sa)数组。可以用前面的hash算法,用set维护。问题在于如何实时维护(ht)数组。假设(sa[k-1]<sa[k]),注意到只有(ht[k]=len-sa[k-1]+1)并且下个读入的字符和(s[sa[k] + len - sa[k-1]+1])相等时(ht[k])需要(+1),((len)表示当前读入的字符串的长度)这是没法优化的,每次修改,则总复杂度为(O(n^2))。考虑把字符串倒置,每次往前面加字符。这样(ht)数组不会改变,而答案显然也正确。

    代码中的(ht)数组定义和先前的不同。(ht[i])表示(i)开始的后缀和(sa[rank[i]-1])开始的后缀的最长公共前缀长度。

    #include <bits/stdc++.h>
    using namespace std;
    namespace cxcyl {
    	const int mod = 1e9 + 7;
    	int n, mi[100005], a[200005], hash[200005], ht[200005];
    	long long sum;
    	inline int read() {
    		int x = 0, f = 1; char c = getchar();
    		while (c > -1 && c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }
    		if (c == -1) return 0;
    		while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
    		return x * f;
    	}
    	inline int equal(int x, int y) {
    		int l = 0, r = min(n - x + 1, n - y + 1), len = r;
    		if (x > y) swap(x, y);
    		while (l < r) {
    			int mid = l + 1 + r >> 1;
    			if ((1ll * mi[y - x] * (hash[x] - hash[x + mid]) % mod + mod) % mod == (hash[y] - hash[y + mid] + mod) % mod)
    				l = mid;
    			else
    				r = mid - 1;
    		}
    		return l;
    	}
    	struct _cmp {
    		inline bool operator()(int x, int y) {
    			int t = equal(x, y);
    			return a[x + t] < a[y + t];
    		}
    	};
    	set<int, _cmp> s;
    	inline int main() {
    		n = read();
    		mi[0] = 1;
    		for (int i = 1; i <= n; ++i)
    			mi[i] = 7ll * mi[i - 1] % mod;
    		for (int i = n; i >= 1; --i) {
    			a[i] = read();
    			hash[i] = (hash[i + 1] + 1ll * mi[i] * a[i]) % mod;
    			if (i < n) {
    				auto x = s.upper_bound(i);
    				if (x == s.end()) {
    					ht[i] = equal(i, *(--x));
    					sum += ht[i];
    				} else if (x == s.begin()) {
    					ht[*x] = equal(*x, i);
    					sum += ht[*x];
    				} else {
    					int v = *x, u = *(--x);
    					ht[i] = equal(u, i);
    					sum += ht[i];
    					sum -= ht[v];
    					ht[v] = equal(i, v);
    					sum += ht[v];
    				}
    			}
    			s.insert(i);
    			printf("%lld
    ", (n - i + 2ll) * (n - i + 1) / 2 - sum);
    		}
    		return 0;
    	}
    } int main() { return cxcyl::main(); }
    

(mathcal{B})

关于整型溢出的问题。加入-ftrapv参数,在有溢出时会程序会崩溃。(Windows中把Dev-c++开成32位的才可用)
别用deque!据说空间会出乎意料的大。

原文地址:https://www.cnblogs.com/herald/p/13950212.html