zoukankan      html  css  js  c++  java
  • 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!据说空间会出乎意料的大。

  • 相关阅读:
    年度回忆录(?——2011.01)
    我在学英语
    技能冷却
    抗锯齿
    在cocos2dx 2.x FPS 等参数
    手指效果
    cocos2dx 简单OpenGL 画图
    cocos2dx tile map瓦片地图的黑线及地图抖动解决方案
    C++操作SQLite数据库
    精灵点击移动
  • 原文地址:https://www.cnblogs.com/herald/p/13950212.html
Copyright © 2011-2022 走看看