[超级码力在线编程大赛初赛(一)] 4.对称前后缀 dp处理回文串

题目链接:对称前后缀

题意

给定一个字符串s。 我们令一个字符串的权值为一个字符串的最长对称前后缀长度。 请求出s的所有子串的权值的总和。 例如,“abcxyzcba” 的最长对称前后缀的长度为3,因为 “abc” 和 “cba” 对称。(注意前后缀也可以是字符串本身)

题解

题中给出 n ≤ 3 ∗ 1 0 3 n≤{3*10^{3}} n3103,并且和回文串有关,想到可以尝试用dp找字符串中最长对称前后缀。
记得前几天写过一个类似相关的题目:Partitioning by Palindromes,是dp预处理找回文子串。

奈 何 自 己 写 过 的 题 还 做 的 这 么 慢 。 > . < {\cancel{奈何自己写过的题还做的这么慢。>.<}} >.<

本题我们来分析,可以先设dp[i][j]:字符串i~j中前后缀的对称字符的个数。
题中写道:单个字符的子串的权值为1,所以初始化:dp[i][i]=1
那么状态转移方程是什么?
dp[i][j]=dp[i+1][j-1]+2 (s[i]==s[j])

很明显,我们可以通过dp[i][j]==(j-i+1)来判断字符串中i~j子串是否为回文串。

知道了这些我们就可以枚举所有子串,如果是回文串,那么它的最长对称前后缀的长度就是它本身dp[i][j];如果不是,那么就是对称字符的个数的一半,dp[i][j]/2。

代码

char str[3005];
long long dp[3005][3005];
class Solution {
    
    
public:
	/**
	 * @param s: a string.
	 * @return: return the values of all the intervals.
	 */
	long long suffixQuery(string &s) {
    
    
		// write your code here
		memset(dp, 0, sizeof(dp));
		for(int i=0;i<s.length();i++) str[i+1]=s[i];
		int n=s.length();
		for(int i=1;i<=n;i++) dp[i][i]=1;
		long long ans=n;
		for(int j=1;j<=n;j++)
			for(int i=j-1;i>=1;i--)
			{
    
    
				if(str[i]==str[j]) dp[i][j]=dp[i+1][j-1]+2;
			}
		for(int i=1;i<n;i++)
			for(int j=i+1;j<=n;j++)
			{
    
    
				if(dp[i][j]==j-i+1) ans+=dp[i][j];
				else ans+=dp[i][j]/2;
			}
		return ans;
	}
};

猜你喜欢

转载自blog.csdn.net/weixin_44235989/article/details/108299576