题意:
给一个数字串A,要求拆解数字串A构造数字串B,满足:
1.A中出现过的数字在B中至少出现一次(假设A中有3个6,那么B中可以有1-3个6)。
2.B不含前导0
问一共有多少种不同的B
例如,给定串A=2028
则B的方案有:208, 280, 802, 820, 2028, 2082, 2208, 2280, 2802, 2820, 8022, 8202, 8220,共13种
思路:
先计算出数字串A中每种数字各有多少个。
dfs枚举每种数字使用的个数。
假设当前数字总数为k,那么全排列方案数为ans=k!
因为相同的数字交换位置是同一种,所以要去重:
ans=ans/(a[0]!*a[1]!*a[2]!*a[3]!*a[4]!...*a[9]!),其中a[i]是数字i出现的次数
另外有0的时候还要减掉前导零的情况:
挑出一个0作为前导零,然后计算剩下的数的方案数(步骤和上面一样),减掉即可
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=25;
char s[maxm];
int fac[maxm];
int cnt[maxm];
int a[maxm];
int ans;
void dfs(int x){
if(x==10){//如果搜到底了
int tot=0;//总数字个数
for(int i=0;i<10;i++){
tot+=a[i];
}
int now=fac[tot];//全排列方案数
for(int i=0;i<10;i++){
now/=fac[a[i]];//去重
}
if(a[0]>=1){//还要排除前导0的情况
int p=fac[tot-1];//挑出一个0放在开头
p/=fac[a[0]-1];
for(int i=1;i<10;i++){//计算剩下的数的方案数
p/=fac[a[i]];
}
now-=p;//减去
}
ans+=now;
return ;
}
for(int i=1;i<=cnt[x];i++){//枚举选取的数字个数
a[x]=i;
dfs(x+1);
}
if(cnt[x]==0)dfs(x+1);
}
signed main(){
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++){//计算数字个数
cnt[s[i]-'0']++;
}
fac[0]=1;
for(int i=1;i<=n;i++){//预处理阶乘
fac[i]=fac[i-1]*i;
}
dfs(0);
cout<<ans<<endl;
return 0;
}