首页 > 其他 > 详细

【未知来源】K-th String

时间:2019-08-20 22:28:13      阅读:99      评论:0      收藏:0      [点我收藏+]

题意

  求有多少种 前 \(n\) 个小写字母的排列 \(t\),满足其所有子串按字典序从小到大排列,第 \(k\) 个子串是一个给定字符串 \(s\)。答案模 \(10^9+7\)
  \(1\le n\le 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\) 个小写字母的位置,不难推出一个公式 \((\sum\limits_{i=1}^{s_1-1} (n-b_i+1)) + |s| = k\)
  \(\sum\limits_{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\) 个比 \(s_1\) 小的字符,当前总共对答案的贡献是 \(k\) 的方案数。注意 \(k\) 这一维状态是 \(O(n^2)\) 级别的。(这个做法中不确定的 \(b_i\) 数量是不确定的,貌似不能预处理 \(\text{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\n",ans);
    return 0;
}

【未知来源】K-th String

原文:https://www.cnblogs.com/scx2015noip-as-php/p/11385496.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!