蓝桥国赛 排列序数增强版!

标题:排列序数

如果用a b c d这4个字母组成一个串,有4!=24种,如果把它们排个序,每个串都对应一个序号:
abcd 0
abdc 1
acbd 2
acdb 3
adbc 4
adcb 5
bacd 6
badc 7
bcad 8
bcda 9
bdac 10
bdca 11
cabd 12
cadb 13
cbad 14
cbda 15
cdab 16
cdba 17

现在有不多于10个两两不同的小写字母,给出它们组成的串,你能求出该串在所有排列中的序号吗?

【输入格式】
一行,一个串。

【输出格式】
一行,一个整数,表示该串在其字母所有排列生成的串中的序号。注意:最小的序号是0。

例如:
输入:
bdca

程序应该输出:
11

再例如:
输入:
cedab

程序应该输出:
70

这个题原题不超过10个,用next_permutation 不超时
但当数据量增大到1e3? 1e5? 虽然没有这么多不同的字符, 但就算是26个字母的话 也会超时 。
现在我们假设最多26个。
先看例子bdca
第一位为b,比b小的有a,所以只要第一位为a 那么后面啥顺序都比原串小。ans= 比第一位小的个数 * 后面位数的阶乘;
然后从第二位开始循环遍历 比d小的有abc,但b在第一位用过了 所以只有ac可用 ,即可用的数量=比当前小的减去前面用过的,但这个数可能为负,和0 max一下。ans+=可用的数量 * 后面位数的阶乘。以此类推。
上代码

#include<iostream>
#include<string.h>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll fact(int x)
{
   ll ans=1;
   for(int i=2;i<=x;i++)
   {
   	ans*=i;
   }
   return ans;
}
int main()
{
   char s[10005];
   scanf("%s",s);
   int book[26]={0};//标记该字母用没用过 
   int len=strlen(s),i,j,cbook;
   int cnt[100];//cnt[i]比i小的有几个 
   char temp[1005];
   strcpy(temp,s);
   sort(temp,temp+len);
   for(i=0;i<len;i++)
   	cnt[temp[i]-'a']=i;
   int t=cnt[s[0]-'a'];
   book[s[0]-'a']=1;
   ll ans=fact(len-1)*t;
   for(i=1;i<len;i++)
   {
   	cbook=0;
   	for(j=0;j<i;j++)
   		if(book[s[j]-'a']&&s[j]<s[i])
   			cbook++;
   	ans=ans+max((ll)0,(cnt[s[i]-'a']-cbook)*fact(len-i-1));
   	book[s[i]-'a']=1;
   }
   cout<<ans;
   return 0;
} 

猜你喜欢

转载自blog.csdn.net/qq_41214789/article/details/89278356