CF432D (2020.3.13训练I题)

Description:
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.

题意:
已知一个只有大写字母组成的字符串,求既是前缀又是后缀的子串数,并求在原串中的出现次数

kmp+dp(核心是动规方程)
先利用kmp的next数组求出所有满足前后缀的子串序号,存入vis数组,定义一个dp数组(注意每个都要初始化为1,dp意思为每种前缀出现的次数)

然后动态转移方程:dp[next[i]]+=dp[i]

输出即可ac

ac代码如下:

#include<iostream>
#include<string>
using namespace std;

const int maxn = 1e5+5;
int nxt[maxn];
int vis[maxn];
char s[maxn];
int dp[maxn];
int len;
void getnext()
{
	nxt[1] = 0;
	for (int i = 2, j = 0; i <= len; i++)
	{
		while (j > 0 && s[j + 1] != s[i]) 
			j = nxt[j];
		if (s[j + 1] == s[i])
			j++;
		nxt[i] = j;
	}
}
int main()
{
	scanf("%s", s + 1);
	len = strlen(s + 1);
	for (int i = 1; i <= len; i++)
	{
		dp[i] = 1;
	}
	getnext();
	int cnt = 0;
	int i = len;
	while (i)
	{
		vis[cnt] = i;
		//cout<<i<<"*"<<endl;
		cnt++;
		i = nxt[i];
	}
	for(int i = len; i > 0; i--)
	{
		dp[nxt[i]] += dp[i];
	}
	cout << cnt << endl;
	for(int i = cnt - 1; i >= 0; i--)
	{
		cout << vis[i] << " " << dp[vis[i]] << endl; 
	}
}
发布了16 篇原创文章 · 获赞 21 · 访问量 1329

猜你喜欢

转载自blog.csdn.net/rainbowower/article/details/104844780