题目链接:https://nanti.jisuanke.com/t/A1873
题目大意:
给你n个字符串,这n个字符串有很多很多种组合,现在你需要从字符串中选择k个字符串进行组合,将所有组合好的串按照字典序从小到大排列,每个字符串从1开始编号,现在给你一个k个字符串组合而成的串,问你这个串在这些排列中的编号是多少?
思路:
首先,我们需要将这个串具体化为一个数字组合,这个数字组合代表我们的当前字符串,那么我们很容易想到了用字典树进行编号,因为字典树可以在字符串结尾附加信息,那么就将这个字符串编号附加到字典树上,再遍历一遍str找出所有val有值的地方,这个值的组合就是str串。
接下来,我们的任务就是,在1到n的数中选择k个数进行排列,现在给你一个k个数的排列,问你这个排列在k个数的所有排列中的字典序位置在哪?
我么考虑具体化的一个数,在5个数中选择3个,问你341这个数的排列在哪?
首先,3这个数之前有1,2,那么ans+=,表明首位安排1和安排2的排列个数
然后,第二位为4,因为之前已经出现过一个小于它的数3,我们呢不能用这个数3,所以比当前4小的数有1,2两个,那么
最后,最后一位为1,那么
最后答案就是30
按照这个思路,对于当前位置的数a[i],我们求出在a[i]之前出现的比a[i]还要小的数的个数num[i],这个可以用树状数组求,那么当前为比它小的可以安排的数量为a[i]-1-num[i],再乘上后面随便安排的数,贡献就是
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
const int mod=1e9+7;
typedef long long ll;
int idx(char c){
return c-'a';
}
struct Trie{
int ch[maxn][26];
int val[maxn];
int sz;
void clear(){
sz=1;
memset(ch[0],0,sizeof(ch[0]));
memset(val,0,sizeof(val));
}
//²åÈë×Ö·û´®
void Insert(string s,int v){
int u=0,n=s.size();
for(int i=0;i<n;i++){
int c=idx(s[i]);
if(!ch[u][c]){
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
val[u]=v;//字符串最后一个字符附加信息
}
//查询
int Find(const char *s,int& st,int len) {
int u = 0;
for(int i = st; i < len; i++) {
if(s[i] == '\0') break;
int c = idx(s[i]);
if(!ch[u][c]) break;
u = ch[u][c];
if(val[u]){
st=i+1;
return val[u];
}
}
return 0;
}
};
Trie trie;
char str[maxn];
int n,k;
vector<int>v;
int c[maxn];
int num[maxn];
//树状数组维护逆序对
int lowbit(int x){
return x&-x;
}
void add(int x,int d){
while(x<=n){
c[x]+=d;
x+=lowbit(x);
}
}
int sum(int x){
int res=0;
while(x>0){
res+=c[x];
x-=lowbit(x);
}
return res;
}
//阶乘数取模
int inv[maxn];
int f[maxn];
int f0[maxn];
void init()
{
inv[0]=inv[1]=1;
f[0]=f[1]=1;
f0[0]=f0[1]=1;
for(int i=2;i<maxn;i++)
{
//阶乘数
f[i]=1ll*f[i-1]*i%mod;
//i在mod意义下的逆元
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
//阶乘逆元
f0[i]=1ll*f0[i-1]*inv[i]%mod;
}
}
//排列数
int A(int a,int b){
if(a==0||b==0)return 1;
return 1ll*f[a]*f0[a-b]%mod;
}
string temp[maxn];
signed main(){
init();
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
while(cin>>n>>k){
trie.clear();
v.clear();
for(int i=1;i<=n;i++){
cin>>temp[i];
}
sort(temp+1,temp+n+1);
for(int i=1;i<=n;i++){
trie.Insert(temp[i],i);//对字符串编号
}
scanf("%s",str);
int len=strlen(str);
int st=0;
while(st<len){
v.push_back(trie.Find(str,st,len));
}
memset(c,0,sizeof(c));
for(int i=1;i<=v.size();i++){
add(v[i-1],1);
num[i]=sum(v[i-1])-1;
}
ll ans=0;
for(int i=1;i<=v.size();i++){
ans+=1ll*(v[i-1]-num[i]-1)*A(n-i,k-i)%mod;
ans%=mod;
}
printf("%lld\n",ans+1);
}
return 0;
}