• 【XSY2344】K-th String(DP+字符串)


    (Description)

    Alice有 n(n≤26) 张牌,牌上分别标有前 n 个英文小写字母。例如,如果 n=3 ,则Alice有3张牌,分别标有"a", "b", "c" 。Alice可以通过排列这些卡牌来构造字符串 t 。考虑字符串 t 的所有子串(共 n(n+1)/2个),按照字典序从小到大排名第 k 的子串为 s 。现在,给你正整数 n,k 和字符串 s ,问有多少种可能的字符串 t 。将答案对 10^9+7 取模。
    
    例如: 当 n=3,t="cab" 时,排序后的子串为"a", "ab", "b", "ca", "cab", "cab",排名第3的子串为"b"。当 n=3,k=3,s="b" 时 ,则 t 可能为"cab"或"bac" ,故答案为2种。
    

    (Input)

    第一行两个整数 n,k(1≤n≤26,1≤k≤n(n+1)/2) 。
    
    第二行一个字符串 s ,s 中仅包含前 n 个字母,且 s 中的字母两两不同。
    

    (Output)

    输出一行表示答案。将答案对 10^9+7 取模。
    

    (Sample Input)

    3 3
    b
    

    (Sample Output)

    2
    

    (HINT)

    数据范围与约定
    
    对于30%的数据, 1≤n≤8
    
    对于所有数据, 1≤n≤26
    

    题解

    由于(t)的每个字符都不相同,那么只要比较首字母就可以知道字符串相对大小,于是我们只需要知道比(s_{1})的字符位置就可以计算对于答案的贡献。

    我们考虑(dp[i][j][k][2]),表示前(i)个字符,已经用了(j)个小于(s_{1})的字符,字典序比(s)小的子串有(k)个,用没用(s)串的方案数

    那么我们就可以枚举(i,j,k,l),随后推出状转方程

    1.第(i)个字符大于等于(s_{1}),所以(j,k,l) 不更新:

    (dp[i+1][j][k][l]+=dp[i][j][k][l])

    2.第(i)个字符小于(s_{1}),显而易见此时状态为(i+1,j+1,k+n-i,l)

    (dp[i+1][j+1][k+n-i][l]+=dp[i][j][k][l])

    3.后面直接接上(s)字符串,此时状态为(i+len,j,k+(n-i)*d2-d,1)

    在这里(d)表示(s)的子串个数,(d2)表示在(s)内比(s_{1})小的字符的个数,((n-i)*d2-d)表示(s)串对答案的贡献(想想为什么是这样):

    (dp[i+len][j][k+(n-i)*d2-d][1]+=dp[i][j][k][l])


    (ps:)细节见代码


    代码·:

    #include<bits/stdc++.h>
    using namespace std;
    const int mod=1e9+7;
    int dp[27][510][510][2];
    char s[30];
    int main()
    {
    	int n,k;
    	scanf("%d %d",&n,&k);
    	scanf("%s",s+1);
    	int len=strlen(s+1);
    	k-=len;//因为s串本身对答案有贡献,于是减去本身的长度
    	if(k<0)
    	{
    		puts("0");
    		return 0;
    	}
    	int d=0,d2=0;//d表示s的子串个数,d2表示在s内比s[1]小的字符的个数
    	int num=s[1]-'a'+1;//在s外比s[1]小的字符的个数
    	for(int i=1;i<=len;i++)
    	{
    		if(s[i]<=s[1])num--;
    		if(s[i]<s[1])
    		{
    			d+=i-1;
    			d2++;
    		}
    	}
    	dp[0][0][0][0]=1;
    	for(int i=0;i<n;i++)
    	{
    		for(int j=0;j<=num;j++)
    		{
    			for(int k=0;k<=(n+1)*n/2;k++)
    			{
    				for(int l=0;l<=1;l++)
    				{
    					dp[i+1][j][k][l]=(dp[i+1][j][k][l]+dp[i][j][k][l])%mod;
    					dp[i+1][j+1][k+n-i][l]=(dp[i+1][j+1][k+n-i][l]+dp[i][j][k][l])%mod;
    					if(!l&&i+len<=n)dp[i+len][j][k+(n-i)*d2-d][1]=(dp[i+len][j][k+(n-i)*d2-d][1]+dp[i][j][k][l]);
    					//没有用过s串并且加上s串不会超过长度
    				}
    			}
    		}
    	}
    	long long ans=dp[n][num][k][1];
    	for(int i=1;i<=num;i++)ans=ans*i%mod;
    	for(int i=1;i<=n-num-len;i++)ans=ans*i%mod;
    	//排列组合
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    [虚拟化/云][全栈demo] 为qemu增加一个PCI的watchdog外设(二)
    PHP中PDO的配置与说明
    PHP输出表格的方法
    打印网格版本9*9乘法表
    js中日历的代码
    js中初学函数的使用
    c#中的23种设计模式
    用多态来实现U盘,Mp3,移动硬盘和电脑的对接,读取写入数据。
    用面向对象多态的思想分别去求圆形和长方形的面积和周长
    面向对象之多态(抽象类)
  • 原文地址:https://www.cnblogs.com/ShuraEye/p/11386611.html
Copyright © 2020-2023  润新知