码迷,mamicode.com
首页 > 其他好文 > 详细

「Codeforces 235C」Cyclical Quest

时间:2020-06-06 21:32:14      阅读:55      评论:0      收藏:0      [点我收藏+]

标签:大于   problem   一个   空间复杂度   结果   简单   str   span   循环   

Description

给定一个字符串 \(S\),以及 \(m\) 个询问。

每个询问给出一个字符串 \(T\),求 \(T\) 的所有循环同构在 \(S\) 中出现的次数总和。

Hint

  • \(1\le n\le 10^5\)
  • \(1\le |S| \le 10^6\)
  • \(1\le \sum|T| \le 10^6\)

Solution

后缀自动机

一个比较简单的思路是,先对 \(S\) 建 SAM,顺便处理出 \(\text{end-pos}\) 集大小。

然后对于每一个 \(T\),将其拆成 \(|T|\) 个字符串分别放在 SAM 上跑,把所有结果求和即可。

但这样一次复杂度为 \(O(|T|^2)\),这样的算法显然不可行。


我们将 \(T\) 复制一遍接在后面,记为 \(R\)

注意到 \(R\) 中包含了所有 \(T\) 的循环同构的字符串,所以可以用 \(R\) 直接在 SAM 上跑

如果当前匹配出的长度大于 \(|T|\),那么 强制失配。因为显然怎么循环都不会有这么长的串,而这个现象是复制所导致的。

为避免应旋转产生相同字符串对答案的影响,可以用一个标记数组。

时间,空间复杂度:\(O(|S| + \sum|T|)\)\(O(|S|)\)。(字符集大小记为常数)

Code

/*
 * Author : _Wallace_
 * Source : https://www.cnblogs.com/-Wallace-/
 * Problem : Codeforces 235C Cyclical Quest
 */
#include <iostream>
#include <map>
#include <string>

using namespace std;
const int N = 1e5 + 5;
const int L = 1e6 + 5;

namespace SAM {
	const int T = L << 1;
	struct Node {
		map<char, int> ch;
		int link, len, size;
	} t[T];
	
	int last;
	int total;
	
	void extend_char(char c) {
		int p = last, np = last = ++total;
		t[np].len = t[p].len + 1;
		
		for (; p && !t[p].ch[c]; p = t[p].link)
			t[p].ch[c] = np;
		
		if (!p) {
			t[np].link = 1;
		} else {
			int q = t[p].ch[c];
			if (t[p].len + 1 == t[q].len) {
				t[np].link = q;	
			} else {
				int nq = ++total;
				t[nq] = t[q], t[nq].len = t[p].len + 1, t[nq].size = 0;
				t[np].link = t[q].link = nq;
				for (; p && t[p].ch[c] == q; p = t[p].link)
					t[p].ch[c] = nq;
			}
		}
		t[np].size = 1;
	}
	
	int b[T], c[T];
	void init_data(string& s) {
		last = total = 1;
		for (string::iterator it = s.begin(); it != s.end(); it++)
			extend_char(*it);
		
		for (register int i = 1; i <= total; i++) ++c[t[i].len];
		for (register int i = 1; i <= total; i++) c[i] += c[i - 1];
		for (register int i = 1; i <= total; i++) b[c[t[i].len]--] = i;
		
		for (register int i = total; i; i--) {
			int x = b[i], f = t[x].link;
			t[f].size += t[x].size;
		}
	}
	
	int vis[T];
	long long count(string& str, int time);
};

long long SAM::count(string& str, int time) {
	long long ret = 0ll;
	int x = 1, len = 0;
	
	str = str + str;
	for (register int i = 0; i < str.size(); i++) {
		char c = str[i];
		while (x && !t[x].ch[c]) x = t[x].link, len = t[x].len;
		if (!x) x = 1, len = 0;
		else x = t[x].ch[c], len++;
		
		while (x && t[t[x].link].len >= str.size() / 2)
			x = t[x].link, len = t[x].len;
		if (len >= str.size() / 2 && vis[x] != time)
			ret += t[x].size, vis[x] = time;
	}
	return ret;
}

signed main() {
	ios::sync_with_stdio(false);
	
	string str;
	cin >> str;
	SAM::init_data(str);
	
	int m;
	cin >> m;
	for (register int time = 1; time <= m; time++) {
		cin >> str;
		cout << SAM::count(str, time) << endl;
	}
	
	return 0;
}

「Codeforces 235C」Cyclical Quest

标签:大于   problem   一个   空间复杂度   结果   简单   str   span   循环   

原文地址:https://www.cnblogs.com/-Wallace-/p/13056348.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!