【代码超详解】Codeforces 432D. Prefixes and Suffixes(KMP 的 next 数组的应用,46 ms)

传送门

一、题目描述

D. Prefixes and Suffixes

time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You have a string s = s1s2…s|s|, where |s| is the length of string s, and si its i-th character.

Let’s introduce several definitions:

A substring s[i..j] (1 ≤ i ≤ j ≤ |s|) of string s is string sisi + 1...sj.
The prefix of string s of length l (1 ≤ l ≤ |s|) is string s[1..l].
The suffix of string s of length l (1 ≤ l ≤ |s|) is string s[|s| - l + 1..|s|]. 

Your task is, for any prefix of string s which matches a suffix of string s, print the number of times it occurs in string s as a substring.

Input

The single line contains a sequence of characters s1s2…s|s| (1 ≤ |s| ≤ 105) — string s. The string only consists of uppercase English letters.

Output

In the first line, print integer k (0 ≤ k ≤ |s|) — the number of prefixes that match a suffix of string s. Next print k lines, in each line print two integers li ci. Numbers li ci mean that the prefix of the length li matches the suffix of length li and occurs in string s as a substring ci times. Print pairs li ci in the order of increasing li.

Examples

Input

ABACABA

Output

3
1 4
3 2
7 1

Input

AAA

Output

3
1 3
2 2
3 1

二、算法分析说明与代码编写指导

先用 KMP 算法 中的求 next 数组的代码对输入的字符串构造 next 数组。

注意:本代码中,字符串第一个字符的下标为1。

题目要求首先输出既是前缀又是后缀的子串个数。设输入的字符串长为 n,我们可以令 i = n + 1,然后不断通过 i = next[i] 向前跳吗,直到 next[i] = 0,就从长到短地统计了全部的相同前后缀并获得总数量。
每个既是前缀又是后缀的子串显然都至少出现了 1 次。为了方便,我们统一把用于统计符合要求的子串的出现次数的数组 c 都填 1。
再令 i = n + 1,然后从后往前遍历直到 i = 0。每一次遍历都要执行 c[next[i]] += c[i]。原因是:
当遍历到某位置 i 时,若 s[i] 之前有既是前缀又是后缀的子串(长度为 next[i] - 1),那么意味着这段字符串在开头和位置 i 之前各出现一次。为什么不直接 += 1 而是 += c[i],是因为这个子串可能也具有相同的前后缀。从后往前跳时,一开始总是将 1 不断累加到最长的相同前后缀的下一个位置(不明白的可以在草稿纸上画图演示)。当跳过这个位置到了前面,自然不能再只累加 1,而是要把前面早已累加的次数也一并加入。
统计完不同长度的相同前后缀的出现次数以后,直接输出即可。注意前面统计不同长度的相同前后缀的数量时是按长度从长到短记录的,所以输出的时候要倒序输出。

三、AC 代码(46 ms)

#include<cstdio>
#include<cstring>
#include<algorithm>
#pragma warning(disable:4996)
template<class _NTy> inline void getnext(const char* const _Pattern, _NTy* const _Next, const size_t& _PatternLen) {
	size_t i = 1; _NTy j = 0; _Next[1] = 0;
	while (i <= _PatternLen) {
		if (j == 0 || _Pattern[i] == _Pattern[j]) { ++i, ++j, _Next[i] = j; }
		else j = _Next[j];
	}
}//The index of the head of _Pattern string is 1.
char s[100002]; unsigned next[100002], c[100002], l[100002], m, n;
int main() {
	gets(s + 1); n = strlen(s + 1); getnext(s, next, n); ++m;
	for (unsigned i = n + 1; next[i] != 0; i = next[i], ++m) { l[m] = i; }
	std::fill(c, c + n + 2, 1);
	for (unsigned i = n + 1; i != 0; --i) { c[next[i]] += c[i]; }
	--m; printf("%u\n", m);
	for (unsigned i = m; i != 0; --i) { printf("%u %u\n", l[i] - 1, c[l[i]]); }
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值