Codeforces Round #448 (Div. 2) D. String Mark

D. String Mark
time limit per test4 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
At the Byteland State University marks are strings of the same length. Mark x is considered better than y if string y is lexicographically smaller than x.

Recently at the BSU was an important test work on which Vasya recived the mark a. It is very hard for the teacher to remember the exact mark of every student, but he knows the mark b, such that every student recieved mark strictly smaller than b.

Vasya isn’t satisfied with his mark so he decided to improve it. He can swap characters in the string corresponding to his mark as many times as he like. Now he want to know only the number of different ways to improve his mark so that his teacher didn’t notice something suspicious.

More formally: you are given two strings a, b of the same length and you need to figure out the number of different strings c such that:

1) c can be obtained from a by swapping some characters, in other words c is a permutation of a.

2) String a is lexicographically smaller than c.

3) String c is lexicographically smaller than b.

For two strings x and y of the same length it is true that x is lexicographically smaller than y if there exists such i, that x1 = y1, x2 = y2, …, xi - 1 = yi - 1, xi < yi.

Since the answer can be very large, you need to find answer modulo 109 + 7.

Input
First line contains string a, second line contains string b. Strings a, b consist of lowercase English letters. Their lengths are equal and don’t exceed 106.

It is guaranteed that a is lexicographically smaller than b.

Output
Print one integer — the number of different strings satisfying the condition of the problem modulo 109 + 7.

Examples
input
abc
ddd
output
5
input
abcdef
abcdeg
output
0
input
abacaba
ubuduba
output
64

题意:给出两个字符串a,b;用a里面的字母去组成字符串c,要求c的字典序大于a小于b,问c的种类
做法:拥有指定数目的字符,用它去构造比目标串x大的串y,可枚举y比x大的决定位置(就是决定它们两个字典序大小的位置),然后计算种类,左后加起来,最开始我是想对于先把给出的字符数目统计一下,当没举到第i位时,因为前面i-1位肯定是相同的,所以可以到还剩下那些字符,枚举第i位要填什么字符,然后再计算用剩下的字符可以构造多少个不同的字符串,计算的方法是res!/(num[i]!(0<=i<26));复杂度是n*26*26,大概是因为计算式longlong型,超时了,然后我把这个代码优化了一下,就是枚举第i位要填什么后计算有多少种可能优化成0(1)的。方法是:因为从上面的那个式子可以知道只有对于枚举的那个字符的num值是不同的,所以用 逆元就可以实现优化了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N= 1e6+100;
const int mod = 1e9+7;
char s[N],t[N];
ll f[N],invf[N],nnf[N];
int num[30];
int num2[30];
int n;

ll qpow(int x,int b){
    ll sum = 1;
    ll now = x;
    while(b){
        if(b&1) sum = sum*now%mod;
        now = now*now%mod;
        b >>= 1;
    }
    return sum;
}
void init(){
    f[0] = 1;
    for(int i =1;i < N;i ++){
        f[i] = f[i-1]*i%mod;
    }
    for(int i = 0;i < N;i ++){
        invf[i] = qpow(f[i],mod-2);
    }
    for(int i = 0;i < N;i ++){
        nnf[i] = qpow(invf[i],mod-2);
    }
}

void add(int &x,int y){
    x += y;
    if(x > mod) x -= mod;
}
int solve(char *st,int len){
    int ret = 0;
    for(int i = 0;i < len;i ++){
        int now = st[i]-'a';
        ll cns = 1;
        for(int j = 0;j < 26;j ++) cns = cns*invf[num[j]]%mod;

        for(int j = now+1;j < 26;j ++){
            if(num[j] == 0)continue;

            int res = len - i-1;
            int tmp = f[res]*cns%mod;
            tmp = tmp*nnf[num[j]]%mod;
            tmp = tmp*invf[num[j]-1]%mod;
            add(ret,tmp);
        }
        //cout <<"!!" << i << ' ' << ret << endl;
        if(num[now] == 0)return ret;
        num[now] --;
    }
    return ret;
}


int main(){
    init();
    scanf("%s %s",s,t);
    n = strlen(s);
    for(int i = 0;i < n;i ++){
        num[s[i]-'a'] ++;
    }

    for(int i = 0;i < 26;i ++) num2[i] = num[i];
    int ans1 = solve(s,n);
    for(int i = 0;i < 26;i ++) num[i] = num2[i];
    int ans2 = solve(t,n);
    bool tmp = true;
    for(int i = 0;i < n;i ++) num2[t[i]-'a'] --;
    for(int i = 0;i < 26;i ++) if(num2[i] != 0) tmp = false;
    if(tmp) ans2 ++;
    //cout << ans1 << ' '<< ans2 << endl;
    cout << (ans1 - ans2 +mod)%mod<<endl;
    return 0;
}

——————————-分界线—————————————————-
有一个更好的做法:虽然也是n*26,但是快很多,因为是int型的运算跑了200ms不到。
做法就是先不管重复的情况,最后再去重。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N= 1e6+100;
const int mod = 1e9+7;
char s[N],t[N];
ll f[N],invf[N],inv[N];
int num[30];
int num2[30];
int n;

ll qpow(int x,int b){
    ll sum = 1;
    ll now = x;
    while(b){
        if(b&1) sum = sum*now%mod;
        now = now*now%mod;
        b >>= 1;
    }
    return sum;
}
void init(){
    inv[1] =  1;
    f[0] =  invf[0] = 1;
    for(int i = 2;i < N;i ++){
        inv[i] = (mod-mod/i)*1LL*inv[mod%i]%mod;
    }
    for(int i =1;i < N;i ++){
        f[i] = f[i-1]*i%mod;
    }
    for(int i = 1;i < N;i ++){
        invf[i] = invf[i-1]*1LL*inv[i]%mod;
    }
}

void add(int &x,int y){
    x += y;
    if(x > mod) x -= mod;
}
int solve(char *st,int len){
    ll ret = 0;
    ll div= 1;
    ll tmp = 1;
    for(int i= 0;i < 30;i ++){
        if(num[i]) div = div*invf[num[i]]%mod;
    }
    for(int i = 0;i < len;i ++){
        int now = st[i]-'a';
        int res = 0;
        for(int j = 0;j < now;j ++){
            res += num[j];
        }
        ret = (ret + tmp*res%mod * f[len - i-1])%mod;
        if(num[now] == 0) break;
        tmp = tmp*num[now]%mod;
            num[now]--;

    }
    ret = ret*div%mod;
    return ret;
}


int main(){
    init();
    int aa = 1;
    scanf("%s %s",s,t);
    n = strlen(s);
    for(int i = 0;i < n;i ++){
        num[s[i]-'a'] ++;
    }
    for(int i = 0;i < 26;i ++) num2[i] = num[i];
    int ans1 = solve(s,n);
    for(int i = 0;i < 26;i ++) num[i] = num2[i];
    int ans2 = solve(t,n);
    cout << (ans2 - ans1 - 1 +mod)%mod<<endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值