小易在学校中学习了关于字符串的理论, 于是他基于此完成了一个字典的项目。
小易的这个字典很奇特, 字典内的每个单词都包含n个'a'和m个'z', 并且所有单词按照字典序排列。
小易现在希望你能帮他找出第k个单词是什么。
小易在学校中学习了关于字符串的理论, 于是他基于此完成了一个字典的项目。
小易的这个字典很奇特, 字典内的每个单词都包含n个'a'和m个'z', 并且所有单词按照字典序排列。
小易现在希望你能帮他找出第k个单词是什么。
输入包括一行三个整数n, m, k(1 <= n, m <= 100, 1 <= k <= 109), 以空格分割。
输出第k个字典中的字符串,如果无解,输出-1。
2 2 6
zzaa
字典中的字符串依次为aazz azaz azza zaaz zaza zzaa
//参考52Hz'大佬C++代码改写为Javaimport java.util.ArrayList;import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scan = new Scanner(System.in); int m = scan.nextInt();//a的个数 int n = scan.nextInt();//z的个数 long target = scan.nextInt();//目标第几个 long k =0; ArrayList<String> list = new ArrayList<String>(); while(m>0&&n>0) {//当a和z均存在时执行 k = pz(m-1,n,target);//假设a确定,出去a之后剩余a和z的排列组合个数 if(k>=target) {//如果确定a之后,剩余的排列组合数大于目标,则说明a已确定 list.add("a"); m--;//a的个数减1 }else {//如果确定a之后,剩余的排列组合数小于目标,则说明不是a。 list.add("z"); n--;//z的个数减1 target -= k;//目标减掉排列组合数。因为如果a开头可以有k中情况, //减掉k之后即为确定z开头之后,接下来找第target个即可。 } } if(target != 1) {//存在经过计算之后必为1 System.out.println("-1"); return; }else { while(m>0) {//如果z的个数为0,则将a追加到最后即可 list.add("a"); m--; } while(n>0) {//如果a的个数为0,则将z追加到最后即可 list.add("z"); n--; } } for (int i = 0; i < list.size(); i++) { System.out.print(list.get(i)); } } public static long pz(int m,int n,long target) {//计算假设a确定之后,a之后的部分排列组合数 if(m==0||n==0) return 1; long sum = m+n; long k = 1; n = Math.min(m, n);//C(m+n) n=C(m+n) m 取最小即可 for (int i = 0; i < n ; i++) { k *= sum-i; k /= (i+1); if(k>target)//防止大数。如果k>target 则只进行list.add("a")和m--//a的个数减1。 //没有target -= k;因此不影响 break; } return k; } }
def Cnm(a, b): ans =1 for i in range(a+1, a + b +1): ans *=i for i in range(1, b +1): ans //=i return ans n, m, k =map(int, input().strip().split()) if Cnm(n, m) < k: print(-1) else: ans ="" while n > 0 and m > 0: temp =Cnm(n -1, m) if temp <k: k-=temp ans +="z" m -=1 else: ans +="a" n -=1 ans +="a"*n ans +="z"*m print(ans)python3的答案,注意除法那里要改成“//”,要不然会溢出报错,参考了上面python2的答案。
//思路,利用字典排序的规律进行选择:
构造一个排列计算函数,计算m+n长度的字符串中,m个z的排列共有多少种可能性C(m,m+n)
首先固定第一位为a,
假如在剩下的m+n-1个位置中选择m个z的位置的排列数小于选取序号k,则说明不在这个排列集合中,此时,可以确定,序号k的数在剩下的排列数中,则第一位序号为z,此时m=m-1,k=k-C(m,m+n-1)
假如选取序号k<C(m,m+n-1),则说明该数在首字母为a的排列数中,此时n=n-1,继续在剩下的字符中按照这种规则进行判断序号k和排列数的关系,直到m和n一方变为0未知
最后,将剩下的m或n补全即可
def C(n, m): ret = 1 for i in range(n + 1, n + m + 1): ret *= i for i in range(1, m + 1): ret //= i return ret if __name__ == "__main__": n, m, k = map(int, input().strip().split(' ')) ans = "" if C(n, m) < k: ans += '-1' else: while n and m: temp = C(n - 1, m) if temp < k: k -= temp ans += 'z' m -= 1 else: ans += 'a' n -= 1 ans += 'a' * n ans += 'z' * m print(ans)
var line = readline().split(' '); var n = parseInt(line[0]), m = parseInt(line[1]), k = parseInt(line[2]); var str = ''; while(n && m){ var cnt = 1; for(var i=0;i<n-1;i++){ cnt *= n-1+m-i; cnt /= i+1; //计算n-1+m个位置放n-1个a if(cnt > k) break; //防止越界。count>k就可以退出计算了 } if(cnt >= k) { //说明首字母就是'a' str += 'a'; n--; //问题缩减为 n-1个a和m个z 中找第k个单词 }else{ str += 'z'; m--; //问题缩减为 n个a和m-1个z 中找第k-cnt个单词 k -= cnt; } } //循环结束后,剩余子序列只存在"aa..aaa" 或 "zz..zzz"1种情况 if(k!=1){ print(-1); }else{ while(n--){ str += 'a'; } while(m--){ str += 'z'; } print(str); }
#include <iostream> #include<cstdio> #include<cstring> #include<vector> #define BIG 1000000000 using namespace std; typedef long long ll; vector<char> res; int ok; // 求全排列C(n,m),如果越界,返回BIG long long Col(ll n, ll m){ m = (n-m)<m?(n-m): m; ll div = m; ll ret = 1; for(ll i=n; i>=n-m+1; i--){ ret *= i; while(ret%div==0 && div!=1) ret/= div--; if(ret<0)//越界啦 return BIG; } return ret; } void solve(ll n, ll m, ll k){ ll cTot = Col(n+m , m); //k大于全排列数,无解 if(cTot < k) { ok = 0; return; } //n==0,或者m==0表示解唯一了啊 if((!n || !m)){ if(n>0) while(n--) res.push_back('a'); else if(m>0) while(m--) res.push_back('z'); return; } ll c0 = Col(n-1+m, m); if(c0 >=k){ //#当前位置取'a' res.push_back('a'); solve(n-1,m, k); } else{ //#当前位置取'z' res.push_back('z'); solve(n,m-1, k-c0); } } int main() { int n,m,k; while(~scanf("%d%d%d",&n, &m, &k)){ ok = 1; res.clear(); solve(n,m,k); if(ok){ for(int i=0; i<res.size(); i++) printf("%c",res[i]); puts(""); } else puts("-1"); } return 0; }
顶我上去,全场最好理解
1.n个'z'和m个‘a’组成的组合数的关系
dp[i][j]=dp[i-1][j]+dp[i][j-1]
2.关于找第几个的问题,递归
固定右边x位,从m-1个z开始固定,a的个数逐渐增加
刚好没有进入下一层递归,就应该是aaazz..zzaaa这种形式
如果不是k不是刚好减去就应该是aa...aazxxxxx后面就进入xxxx里面再做一层递归
import java.util.*; public class Main { public static void main(String[] args) { Scanner sc=new Scanner(System.in); int n=sc.nextInt(); int m=sc.nextInt(); int k=sc.nextInt(); long[][] dp=new long[101][101]; //dp[i][j]i个'a'和j个'b'的组合数 for(int i=0;i<101;i++) dp[i][0]=1; for(int j=1;j<101;j++) dp[0][j]=1; for(int i=1;i<101;i++) { for(int j=1;j<101;j++) { dp[i][j]=dp[i-1][j]+dp[i][j-1]; } } if(dp[n][m]<k) { System.out.println("-1"); return; } Main main=new Main(); StringBuilder sb=new StringBuilder(); main.solve(n, m, k, sb, dp); } public void solve(int a,int z,int k,StringBuilder sb,long[][] dp) { if(a==0) { for(int i=0;i<z;i++) sb.append("z"); System.out.println(sb.toString()); return; } if(z==0) { for(int i=0;i<a;i++) sb.append("a"); System.out.println(sb.toString()); return; } int la=0,lz=z-1; while(k-dp[la][lz]>=0) { k-=dp[la][lz]; la++; } if(k==0) { for(int i=0;i<a-la+1;i++) sb.append("a"); for(int i=0;i<z;i++) sb.append("z"); for(int i=0;i<la-1;i++) sb.append("a"); System.out.println(sb.toString()); return; } for(int i=0;i<a-la;i++) sb.append("a"); sb.append("z"); solve(la,lz,k,sb,dp); } }
""" n个'a'和m个'z',排列组合有C(n,m)种结果 利用以上性质找到字典序第k个单词 """ import sys def C(n, m): ret = 1 for i in range(n + 1, n + m + 1): ret *= i for i in range(1, m + 1): ret //= i return ret if __name__ == "__main__": # sys.stdin = open('input.txt', 'r') n, m, k = list(map(int, input().strip().split())) ans = "" if C(n, m) < k: ans += '-1' else: while n and m: temp = C(n - 1, m) if temp < k: k -= temp ans += 'z' m -= 1 else: ans += 'a' n -= 1 ans += 'a' * n ans += 'z' * m print(ans)
import math n, m, k =list(map(int,input().split())) def C(n,m): #组合公式 return math.factorial(n+m)/(math.factorial(n)*math.factorial(m)) r = [] def solve(n,m,k): if n ==0: for i in range(m): r.append('z') return if m ==0: for i in range(n): r.append('a') return if k > C(n-1,m): #以a开头可能的字符串为C(n-1,m)种,均排在以z开头前, r.append('z') solve(n,m-1,k-C(n-1,m)) #以z开头,问题转为剩余字串中第k-C(n-1,m)个 else: r.append('a') solve(n-1,m,k) if k> max_k: print(-1) else : solve(n,m,k) print(''.join(r))
n,m,k=list(map(int,input().split()))nn=n;mm=ml=[]jc=[1]for i in range(m+n):jc.append((i+1)*jc[i])if k>jc[n+m]/(jc[m]*jc[n]):print('-1')else:for i in range(1,mm+nn+1):an=jc[m+n-1]/(jc[n-1]*jc[m])if k<=an:l.append('a')n-=1else:l.append('z')m-=1k=k-anl=''.join(l)print(l)
// 读取
const input = readline().split(/\s/)
const n = parseInt(input[0])
const m = parseInt(input[1])
const k = parseInt(input[2])
let answer
let queue = []
let i = 0
let aDone = false
// 跳床
function trampoline(f){
var func = f;
while (typeof func === 'function'){
func = func();
}
return func;
}
// 递归
// 先考虑a, 再考虑z
function getItem (n, m, ret) {
if (n === 0 && m === 0) {
++i
if (i === k) {
answer = ret
aDone = true
queue.length = 0
return
}
if (queue.length > 0) {
let _ret = queue.pop()
let _m = queue.pop()
let _n = queue.pop()
return getItem.bind(null, _n, _m, _ret)
}
}
if (n > 0 && m > 0) {
queue.push(n, m - 1, ret.concat('z'))
return getItem.bind(null, n - 1, m, ret.concat('a'))
} else if (n > 0) {
return getItem.bind(null, n - 1, m, ret.concat('a'))
} else if (m > 0) {
return getItem.bind(null, n, m - 1, ret.concat('z'))
}
}
// 全排列 [aazz azaz azza zaaz zaza zzaa]
// 递归改循环, 避免js内存溢出
const getList = () => {
// a开头
trampoline(getItem.bind(null, n - 1, m, 'a'))
// z开头
!aDone && trampoline(getItem.bind(null, n, m - 1, 'z'))
}
// 查找 ? xxxx : -1
getList()
print(answer || -1)
1 40%, 超时
2 循环大数据时CPU超100%
3 谁能用js做出
100 100 Math.pow(10, 9)
或者说 100%的 大神 麻烦贴下代码, 先给您跪了
#include <bits/stdc++.h> using namespace std; #define ll long long int main(){ int n,m; ll k; while(cin>>n>>m>>k){ string s = ""; while(n && m){ ll cnt = 1; for(int i=0;i<n-1;i++){ cnt *= (n-1+m-i); cnt /= (i+1); if(cnt>k) break; } if(k<=cnt){ s += 'a'; n--; }else{ s += 'z'; m--; k -= cnt; } } if(k!=1) cout<<-1<<endl; else{ while(n--) s += 'a'; while(m--) s += 'z'; cout<<s<<endl; } } return 0; }
#include<iostream> using namespace std; /*c[a][z] 表示a个'a',z个'z'共有的组合数*/ int c[101][101]; /*递归从左只右计算每个'z'的前面有几个'a'并打印*/ void print(int a,int z,int k) { if(a==0) { for(int i=0;i<z;i++) cout<<'z'; return; } if(z==0) { for(int i=0;i<z;i++) cout<<'a'; return; } int la=0,lz=z-1; while(k-c[la][lz]>=0) { k-=c[la][lz]; la++; } if(k==0) { for(int i=0;i<a-la+1;i++) cout<<'a'; for(int i=0;i<z;i++) cout<<'z'; for(int i=0;i<la-1;i++) cout<<'a'; return; } for(int i=0;i<a-la;i++) cout<<'a'; cout<<'z'; print(la,lz,k); } int main() { int n,m,k; cin>>n>>m>>k; int i,j; /*计算所有情况的组合数(1=<a,z<=100,由于k<10^9所以不需要超过int范围的组合数的计算,所以此处溢出的数直接表示为int_max)*/ for(i=0;i<101;i++) { c[0][i]=1; c[i][0]=1; } for(i=1;i<101;i++) { for(j=1;j<101;j++) { c[i][j]=c[i-1][j]+c[i][j-1]; if(c[i][j]<0) { c[i][j]=0x7FFFFFFF; } } } if(c[n][m]<k) { cout<<c[n][m]<<" "<<k<<endl; cout<<-1; return 0; } print(n,m,k); return 0; }
排列组合,n个'a'和m个'z',只能组成$C_{n+m}^n$,记为count(n+m,n) 个单词。
思路:
eg:n=2,m=2,k=5
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
void solve(int n, int m, long long k) {
vector<char> x;//
while (n && m) {
//每次迭代问题规模缩减一个单位
////排列组合:假设当前序列首字符为a,剩下n-1个a放在剩下n - 1 +m 个位置共有的可能数
long long count = 1;
for (int i = 0; i < n - 1; i++) {//求组合数
count *= n - 1 + m - i;
count /= (i + 1);
if (count > k)break;//防止越界。count>k就可以退出计算了
}
if (k <= count) {//如果k小于等于count,则表明首字符的确应为a
x.push_back('a');
n--;//问题缩减为 n-1个a和m个z 中找第k大
}
else {
x.push_back('z');
m--;//问题缩减为 n-1个a和m个z 中找第k-count大
k -= count;
}
}
//循环结束后,剩余子序列只存在"aa..aaa" 或 "zz..zzz"1种情况
if (k != 1) {//
cout << -1;
return;
}
while (n--)x.push_back('a');
while (m--)x.push_back('z');
for (int i = 0; i < x.size(); i++) {
cout << x[i];
}
}
};
int main() {
Solution a;
int n, m;
long long k;
while (cin >> n >> m >> k) {
a.solve(n, m, k);
}
return 0;
}
import math n, m, k = map(int, input().split()) def comb(n, k): k = min(k, n - k) res = 1 a, b = 1, n for _ in range(k): res *= b/a a += 1 b -= 1 return round(res) cur = 1 res = '' while n > 0 and m > 0: c = comb(n + m -1, n - 1) if c <= k - cur: cur += c m -= 1 res += 'z' else: n -= 1 res += 'a' if cur == k: break if cur != k: print(-1) else: res += n*'a' + m*'z' print(res)
递归解法,每次确定一位字母
string _findk(int n, int m, int k){ if(n==0){ return string(m,'z'); }else if(m==0){ return string(n, 'a'); } n--; long long cnt = 1; for(int i=0; i<n; i++){ cnt *= (m+n-i); cnt /= (i+1); if(cnt>k) break; } if(cnt>=k){ return "a" + _findk(n, m, k); }else{ if(cnt*(n+m+1)/(n+1)<k) return "-1"; return "z" + _findk(n+1, m-1, k-cnt); } } void _azword(){ int n,m,k; cin >> n >> m >> k; cout << _findk(n,m,k) << endl; }
/* count(n,m)=count(n-1,m)+count(n,m-1) count(0,k)=1 count(k,0)=1 */ #include<bits/stdc++.h> using namespace std; const long long maxValue=1e9; int main() { string res=""; int n,m; long long k; cin>>n>>m; cin>>k; vector<vector<long long >>count(n+1,vector<long long>(m+1,0)); for(int i=0;i<=n;i++) for(int j=0;j<=m;j++) { if(i==0||j==0) { count[i][j]=1; } else { if(count[i-1][j]+count[i][j-1]>=maxValue) { count[i][j]=maxValue; } else { count[i][j]=count[i-1][j]+count[i][j-1]; } } } int i=n; int j=m; if(count[i][j]<k) { cout<<-1<<endl; return 0; } while(i&&j) { if(i>0) { if(k<=count[i-1][j]) { res=res+'a'; i--; } else if(j>0) { res=res+'z'; k=k-count[i-1][j]; j--; } } } while(i>0) { res=res+'a'; i--; } while(j>0) { res=res+'z'; j--; } cout<<res<<endl; return 0; }
import java.io.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] line = br.readLine().split(" "); int n = Integer.parseInt(line[0]); int m = Integer.parseInt(line[1]); int k = Integer.parseInt(line[2]); // 特判 if (k > combination(n, n + m, k)) { System.out.print(-1 + ""); return; } StringBuilder sb = new StringBuilder(); while (n > 0 && m > 0) { // 开头为 a 的组合数 long c = combination(n - 1, n + m - 1, k); if (k > c) { // 大于则以 z 开头 sb.append("z"); k -= c; m--; } else { // 小于则以 a 开头 sb.append("a"); n--; } } // 结尾处理 if (n > 0) { while (n > 0) { sb.append("a"); n--; } } else { while (m > 0) { sb.append("z"); m--; } } System.out.print(sb); } public static long combination(int n, int m, int k) { long c = 1; for (int i = 0; i < n; i++) { c *= m - i; c /= i + 1; if (c > k) break; } return c; } }
#include <iostream> (720)#include <vector> #include <algorithm> using namespace std; bool isSwap(char *from, char * to) { char * p; for (p = from; p < to; p++) { if (*p == *to) { return false; } } return true; } void helper(vector<char> & vec, int from, int to, int &k) { if (to <= 1) { return; } if (from == to) { if (k-- == 1) { for (int i=0; i <= to; i++) { cout << vec[i]; } cout << endl; } } else { for (int j = from; j <= to; j++) { if (isSwap(&vec[from], &vec[j])) { swap(vec[j], vec[from]); helper(vec, from + 1, to, k); swap(vec[j], vec[from]); } } } } int main() { int n, m, k; cin>>n>>m>>k; vector<char> vec; while(n--){ vec.push_back('a'); } while(m--){ vec.push_back('z'); } helper(vec, 0, vec.size()-1, k); return 0; }