题意:给定一个长度在[1,14],由小写字母组成,不能同字母数至多10个的字符串,每种字母可以任意取数字映射,但不同字母对应的数字不能相同,相同字母对应的数字必须相同(注意:转换的数字不能有前导0)。问给定字符串所有映射数的最大公约数是多少?输出这个最大公约数的所有因子。例如abab可以构成1010,2323,7575等等,最大公约数为101.
题解:
没找到什么特殊的方法,只能暴力下,枚举了所有数字组合,当然这样肯定超时间,所以需要限定枚举次数,这种属于概率型,还是成功了,哈哈。最后的输出达到1e14,所以需要先预处理1e7的素数,然后找出所有质因子,然后用go()函数输出所有因子。
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#include <iostream>
#include <queue>
#include <ctime>
#include <cstdlib>
using namespace std;
#define LL __int64
const int maxn=250;
const int maxm=1e6;
char a[maxn];
int c[maxn];
int n,m,t,num,tot,k;
int prime[maxm],vis[10],d[10],g[10000],gg,f[10000],ff;
bool check[10000010];
LL ret[10000],res,ans;
LL gcd(LL a,LL b)//求最大公约数
{
return b==0?a:gcd(b,a%b);
}
/*void random(int x)
{
int i,j,k;
x=10;
srand((unsigned)time(NULL));
for(i=0;i<x;i++)
b[i]=i;
m=x;
for(i=0;i<x;i++)
{
int p=rand()%m;
while(i==0&&p==0)p=rand()%m;
f[i]=b[p];
b[p]=b[m-1];
m--;
}
for(i=0;i<x;i++)
printf("%d ",f[i]);
printf("\n");
}*/
void init()//预处理,找出所有1e7以内的素数,以减少查找1e14范围数的因子的时间
{ //现行筛素数的方法,时间复杂度为O(n)
memset(check,false,sizeof(check));
int i,j;
tot=0;
for(i=2;i<=1e7;i++)
{
if(!check[i])prime[tot++]=i;
for(j=0;j<tot;j++)
{
if(i*prime[j]>1e7)break;
check[i*prime[j]]=true;
if(i%prime[j]==0)break;
}
}
//printf("%d\n",tot);
//for(i=0;i<20;i++)
// printf("prime[%d]:%d\n",i,prime[i]);
}
void dfs(int x)//枚举所有字母对应的数字
{
if(k>84000)return;//限定次数,概率型水着题目,k值可以取到140000。
if(x==t)
{
k++;
res=0;
for(int i=0;i<n;i++)
res=res*10+d[c[a[i]-'a']];
ans=gcd(ans,res);
return ;
}
int i,j;
for(i=(x==0?1:0);i<10;i++)
{
if(vis[i])continue;
d[x]=i;
vis[i]=1;
dfs(x+1);
if(k>84000)return;
vis[i]=0;
}
}
void go(int now,int dep,LL mul)//通过质因子递归枚举所有因子
{
if(now==dep)
{
ret[num++]=mul;
return;
}
for(int i=0;i<=f[now];i++)
{
go(now+1,dep,mul);
mul*=g[now];
}
}
int main()
{
init();
int T,tt=0;
scanf("%d",&T);
LL p,q;
while(T--)
{
int i,j;
scanf("%s",a);
n=strlen(a);
memset(c,-1,sizeof(c));
t=0;
for(i=0;i<n;i++)
{
if(c[a[i]-'a']==-1)c[a[i]-'a']=t++;
}
//random(t);
ans=0;
for(i=0;i<n;i++)
ans=ans*10+(c[a[i]-'a']+1)%10;
memset(vis,0,sizeof(vis));
k=0;
dfs(0);
//找出所有的质因子以及个数
num=0;
gg=0;
for(i=0;prime[i]*prime[i]<=ans;i++)
{
if(ans%prime[i])continue;
ff=0;
while(ans%prime[i]==0)
{
ff++;
ans=ans/prime[i];
}
g[gg]=prime[i];
f[gg]=ff;
gg++;
}
if(ans>1)
{
g[gg]=ans;
f[gg]=1;
gg++;
}
//找出所有因子
go(0,gg,1);
//输出
sort(ret,ret+num);
printf("Case %d: %I64d",++tt,ret[0]);
for(i=1;i<num;i++)
if(ret[i]!=ret[i-1])
printf(" %I64d",ret[i]);
printf("\n");
}
return 0;
}