Codeforces Round #449 (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
inputCopy
abc
ddd
output
5
inputCopy
abcdef
abcdeg
output
0
inputCopy
abacaba
ubuduba
output
64
Note
In first sample from string abc can be obtained strings acb, bac, bca, cab, cba, all of them are larger than abc, but smaller than ddd. So the answer is 5.

In second sample any string obtained from abcdef is larger than abcdeg. So the answer is 0.

题意

给出两个字符串a和b,问a的全排列中有多少字符串字典序大于a且小于b。

题解

果然codeforces上的题目大多都是分析加瞎搞能做出来的。
一开始理解错题意,以为没有重复,后来才发现重复是不算的。
下面是我的做法:
实际上非常容易的题目。
- 先考虑这个问题本身,询问符合某条件的字符串个数,而且关于字典序,所以想当然的往数位dp上靠拢了,于是想到用小于b的所有数减去小于a的所有数(这里数指字符串个数)。
- 进一步分析,可以用一个dfs()一位一位枚举,然后对于一个位,放小于它的数的贡献可以在O(26)内求得,而放这个位本身这个字母则能推到下一个位继续求贡献。
- 然后发现这个过程可以用一遍for循环解决。
于是有了下面代码。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <vector>
#include <cstring>
#include <complex>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
const int INF = 0x3f3f3f3f;
const int mod = 1e9+7;
const int maxn =1e6+7;
const double pi = acos(-1.0);
const int v2 = (mod+1)/2;
char a[maxn],b[maxn];
int len,ans1,ans2,vec[30],A[maxn],inv[maxn];
ll qpow(ll a,ll b)
{
    ll res = 1;
    while(b)
    {
        if(b&1) res = (res*a)%mod;
        a = (a*a)%mod;
        b>>=1;
    }
    return res;
}

void init()
{
    A[0] = A[1] = 1;
    inv[0] = inv[1] = 1;
    for(int i = 2;i < maxn;i++)
    {
        A[i] = 1ll*A[i-1]*i%mod;
        inv[i] = 1ll*inv[i-1]*qpow(i,mod-2)%mod;
    }
}
void initial()
{
    memset(vec,0,sizeof(vec));
    for(int i = 0;i < len;i++)
    {
        vec[a[i]-'a'+1]++;
    }
}

int main()
{
    init();
    scanf("%s",a);
    scanf("%s",b);
    len = strlen(a);
    initial();
    for(int i = 0;i < len;i++)
    {
        int now = a[i]-'a'+1;
        ll tmp = A[len-i-1];
        for(int k = 1;k <=26;k++)
        {
            if(vec[k]) tmp = tmp*inv[vec[k]]%mod;
        }
        for(int j = 1;j < now;j++)
        {
            if(vec[j]!=0)
            {
                tmp = (tmp*A[vec[j]]%mod)*inv[vec[j]-1]%mod;
                ans1 = (ans1+tmp)%mod;
                tmp = (tmp*A[vec[j]-1]%mod)*inv[vec[j]]%mod;
            }
        }
        if(vec[now]==0) break;
        else
        {
            vec[now]-=1;
        }
    }
    initial();
    for(int i = 0;i < len;i++)
    {
        int now = b[i]-'a'+1;
        ll tmp = A[len-i-1];
        for(int k = 1;k <=26;k++)
        {
            if(vec[k]) tmp = tmp*inv[vec[k]]%mod;
        }
        for(int j = 1;j < now;j++)
        {
            if(vec[j]!=0)
            {
                tmp = (tmp*A[vec[j]]%mod)*inv[vec[j]-1]%mod;
                ans2 = (ans2+tmp)%mod;
                tmp = (tmp*A[vec[j]-1]%mod)*inv[vec[j]]%mod;
            }
        }
        if(vec[now]==0) break;
        else
        {
            vec[now]-=1;
        }
    }
    printf("%d\n",((ans2-ans1-1)%mod+mod)%mod);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值