题目:
链接:https://www.nowcoder.com/acm/contest/156/D
来源:牛客网
题目描述
托米没有完成上一个任务,准备施展黑魔法推倒 1317
黑魔法咒语被描述为一个 长为 n 的,仅包含小写英文字母 'a'...'i' 的字符串,在托米所在的星球,魔法造成的每次有效伤害都是来自他的一个子序列,对于每一个 'a'... 'i' 的排列(共 9! 种),若作为咒语的子序列出现, 就会造成 1 的伤害
而咒语的总伤害为所有 'a'... 'i' 的排列造成的伤害值之和,托米能打出多少点的伤害,是否能击败 1317 呢?
输入描述:
一行输入一个字符串 s
输出描述:
一行输出一个数,表示伤害值
示例1
输入
aabcdefghi
输出
1
备注:
|s| ≤ 3000
思路:
补题ing,为了督促自己补完这题所以先把题目发在题解里。
比赛时没看懂这题啥意思,然后看了一下题解,大概是给你一个字符串,问它的子串有多少个是'a'-'i'的全排列中的字符串。
暴力了一下发现太慢了。
然后参考了一下题解https://blog.csdn.net/haut_ykc/article/details/81259754过于优秀……反正我是想不到这么妙的方法。
补充说明一下:
记录每一个位置开始时每个字母第一次出现的位置。这样可以把O(n)变成O(10)。
dp[i][k]表示从第i个字符开始字母k最早出现的位置。
比如给你一个样例bbabcdefg
那么在第一个字符开始的时候,字母a第一次出现的位置是3(从1开始),那就从第二个位置开始找下一个字母b,发现没有字母b,于是跳出这个排序,生成下一个全排列。全排列用STL里的next_permutation就好。
像这个样例用dp表示就是 dp[0][0]=3,dp[0][1]=1,dp[0][2]=5……dp[i][k]中的k表示字母,a就是0,b就是1,记录时用字母减去'a'就行。
代码:
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
int main()
{
char a[3001];
while(scanf("%s",a+1)!=EOF){
int dp[3001][10],num[10];
char ss[]="abcdefghi";
memset(num,0,sizeof(num));
for(int i=strlen(a+1);i>=0;i--){
for(int j=0;j<9;j++){
dp[i][j]=num[j];
}
if(i)num[a[i]-'a']=i;
}
int ans=0;
do{
int k=0;
for(int i=0;i<9;i++){
k=dp[k][ss[i]-'a'];
if(!k)break;
}
if(k)ans++;
}while(next_permutation(ss,ss+9));
printf("%d\n",ans);
}
return 0;
}