【备战蓝桥杯】3.字串分值(50分)——动态规划DP

298 阅读1分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

对于一个字符串 S,我们定义 S 的分值 f(S ) 为 S 中出现的不同的字符个 数。例如 f(”aba”) = 2,f(”abc”) = 3, f(”aaa”) = 1。 现在给定一个字符串 S [0..n − 1](长度为 n),请你计算对于所有 S 的非空 子串 S [i.. j](0 ≤ i ≤ j < n),f(S [i.. j]) 的和是多少。

【输入格式】

输入一行包含一个由小写字母组成的字符串 S。

【输出格式】

输出一个整数表示答案。

【样例输入】

ababc

【样例输出】

28

【样例说明】

子串 f值
a 1
ab 2
aba 2
abab 2
ababc 3
b 1
ba 2
bab 2
babc 3
a 1
ab 2
abc 3
b 1
bc 2
c 1

【评测用例规模与约定】

对于 20% 的评测用例,1 ≤ n ≤ 10;
对于 40% 的评测用例,1 ≤ n ≤ 100;
对于 50% 的评测用例,1 ≤ n ≤ 1000;
对于 60% 的评测用例,1 ≤ n ≤ 10000;
对于所有评测用例,1 ≤ n ≤ 100000。

思路

第一眼看见题目就感觉像是动态规划,O(n2)O(n^2) 解法。不过显然只能得一半的分。然而实在想不出更好的解法了,先把动态规划的方法列出来把,反正比赛的时候一般也就只能想到DP做法了。

建立dp1dp1 三维数组,dp1[i][j]dp1[i][j] 表示子串S[i...j]S[i...j]的各个字母的状态,状态压缩为int整形,从第0位到第25位分别是a至z的状态。若dp1[i][j][0]dp1[i][j][0]的某一位为0,则代表该子串中没有对应字符;若dp1[i][j][0]dp1[i][j][0]的某一位为1,则再看dp1[i][j][1]dp1[i][j][1]的这一位,若为0,则代表该子串中对应的字符出现1次,若为1,则代表该子串中对应的字符出现多次。

dp2[i][j]dp2[i][j]就代表该子串的f的和。

举例:对于字符串abccdddefdp1[1][3][0]=0b0...01110,dp1[1][3][1]=0b0...01100,dp2[1][3]=1abccdddef,dp1[1][3][0]=0b0...01110,dp1[1][3][1]=0b0...01100,dp2[1][3]=1.

最后递推公式就很好推出了,详见代码。

代码

//本代码应该只能过一半 
#include<bits/stdc++.h>

using namespace std;
const int maxn=1005;
int dp1[maxn][maxn][2];//状态 
int dp2[maxn][maxn];//数量 
char str[maxn*100];
int main(){
	cin>>str;
	if(strlen(str)>1000)return 0;
	int len=strlen(str);
	for(int i=0;i<len;i++){
		dp1[i][i][0]=1<<(str[i]-'a');
		dp2[i][i]=1;
	}
	for(int d=1;d<len;d++){
		for(int i=0;i<len-1;i++){
			int j=i+d;
			if((dp1[i][j-1][0]>>(str[j]-'a'))%2==1){
				if((dp1[i][j-1][1]>>(str[j]-'a'))%2==1){
					dp1[i][j][0]=dp1[i][j-1][0];
					dp1[i][j][1]=dp1[i][j-1][1];
					dp2[i][j]=dp2[i][j-1];
				}else{
					dp1[i][j][0]=dp1[i][j-1][0];
					dp1[i][j][1]=dp1[i][j-1][1]+(1<<(str[j]-'a'));
					dp2[i][j]=dp2[i][j-1]-1;
				}
			}else{
				dp1[i][j][0]=dp1[i][j-1][0]+(1<<(str[j]-'a'));
				dp1[i][j][1]=dp1[i][j-1][1];
				dp2[i][j]=dp2[i][j-1]+1;
			}
		}
	}
	int cnt=0;
	for(int i=0;i<len;i++){
		for(int j=i;j<len;j++){
			cnt+=dp2[i][j];
		}
	}
	cout<<cnt;
	return 0;
}

总结

这题出现在蓝桥杯大题第三题,又刷新了我对于蓝桥杯难度的题解。作为一个将近3个月没有碰算法的人来说,确实该好好准备一下了。