zoukankan      html  css  js  c++  java
  • 【未知来源】K-th String

    题意

      求有多少种 前 (n) 个小写字母的排列 (t),满足其所有子串按字典序从小到大排列,第 (k) 个子串是一个给定字符串 (s)。答案模 (10^9+7)
      (1le nle 26),保证 (s) 中仅包含前 (n) 个小写字母,且 (s) 中的字母两两不同。
      sample input:3 3 b
      sample output:2

    题解

      由于排列 (t) 的每个字符都互不相同,显然只需要比较首字母就能知道字典序的大小。
      于是确定比 (s_1) 小的那些字符的位置即可。

      下面把将字符串 (s) 的排名往后挤的名次称为对答案的贡献
      (O(n)) 枚举字符串 (s) 的位置,然后发现要求 (s) 内部比 (s_1) 小的字符对答案的贡献,(O(n)) 算一下即可。
      设 (b_i) 表示第 (i) 个小写字母的位置,不难推出一个公式 ((sumlimits_{i=1}^{s_1-1} (n-b_i+1)) + |s| = k)
      (sumlimits_{i=1}^{s_1-1} (n-b_i+1)) 表示所有小于 (s_1) 的字符对答案的贡献,常数 (|s|) 表示字符串 (s) 是以 (s_1) 开头的所有字符串的第 (|s|) 小、
      字符串 (s) 中可能已有一些小于 (s_1) 的字符,我们把它对应的 (b_i) 变成确定的常数。
      我们把所有常数(包括 (|s|)、确定的 (b_i)(n-b_i+1) 中的 (n+1))挪到等号右边,合并为一个常数。那么对于其余在 (s) 以外的小于 (s_1) 的字符,问题变成 求有多少种方案选择 (x)未确定的 (b_i),使得它们的和为一个常数 (C)
      这是普及组背包问题。设 (dp(i,j,k)) 表示前 (i) 个未填位置,已经用了 (j)(b_i),当前总共对答案的贡献是 (k) 的方案数。注意 (k) 这一维状态是 (O(n^2)) 级别的。(这个做法中不确定的 (b_i) 数量是不确定的,貌似不能预处理 ( ext{dp})
      还要注意一点,排列是有标号的,所以对 (s) 以外的部分做背包后,小于 (s_1) 的部分和其余部分的方案数要各乘一个阶乘。
      总复杂度 (O(n^5))。应该有个 (O(n^4)) 的做法,不过我是鸽子。

    #include<bits/stdc++.h>
    #define ll long long
    #define N 27
    #define mod 1000000007
    using namespace std;
    inline int read(){
    	int x=0; bool f=1; char c=getchar();
    	for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
    	for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
    	if(f) return x; return 0-x;
    }
    int jc[N]; 
    int n,m,len,ans;
    char s[N];
    namespace Pack{
    	int x,a[N],dp[N][N*N];
    	inline void add(int v){a[++x]=v;}
    	inline void clr(){x=0;}
    	int solve(int num, int sum){
    		if(m<0) return 0;
    		for(int i=0; i<=num; ++i)
    			for(int j=0; j<=sum; ++j) dp[i][j]=0;
    		dp[0][0]=1;
    		for(int i=1; i<=x; ++i)
    			for(int j=min(num,i); j>=1; --j)
    				for(int k=sum; k>=a[i]; --k)
    					dp[j][k] = (dp[j][k] + dp[j-1][k-a[i]]) % mod;
    		return (ll)dp[num][sum] * jc[num] % mod * jc[x-num] % mod;
    	}
    }using namespace Pack;
    int main(){
    	jc[0]=1; for(int i=1; i<=26; ++i) jc[i]=(ll)jc[i-1]*i%mod;
    	n=read(), m=read(); scanf("%s",s+1), len=strlen(s+1);
    	for(int i=1; i<=len; ++i) s[i]-=96;
    	for(int i=1; i<=n-len+1; ++i){
    		int sum=m-len, num=s[1]-1;
    		for(int j=1; j<=len; ++j) if(s[j]<s[1]) sum-=(n-(i-1+j)+1), --num;
    		for(int j=1; j<i; ++j) add(n-j+1);
    		for(int j=i+len; j<=n; ++j) add(n-j+1);
    		ans=(ans+solve(num,sum))%mod;
    		clr();
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    du 命令计算隐藏文件夹或文件
    QEMU中VIRTIO实现
    virtio_blk
    网络虚拟化Virtio-net
    virtio desc
    vhost dpdk 共享内存
    gvisor 信号处理
    Java idea 执行单个测试方法
    Java idea 创建User.xml,需要新增一个mybatis-mapper.xml模板
    Java idea 创建log4j.properties
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/11385496.html
Copyright © 2011-2022 走看看