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。
思路
第一眼看见题目就感觉像是动态规划, 解法。不过显然只能得一半的分。然而实在想不出更好的解法了,先把动态规划的方法列出来把,反正比赛的时候一般也就只能想到DP做法了。
建立 三维数组, 表示子串的各个字母的状态,状态压缩为int整形,从第0位到第25位分别是a至z的状态。若的某一位为0,则代表该子串中没有对应字符;若的某一位为1,则再看的这一位,若为0,则代表该子串中对应的字符出现1次,若为1,则代表该子串中对应的字符出现多次。
就代表该子串的f的和。
举例:对于字符串.
最后递推公式就很好推出了,详见代码。
代码
//本代码应该只能过一半
#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个月没有碰算法的人来说,确实该好好准备一下了。