【2014微软实习生笔试】2:找到第k个字符串


Time Limit: 10000ms
Case Time Limit: 1000ms
Memory Limit: 256MB

Description

Consider a string set that each of them consists of {0, 1} only. All strings in the set have the same number of 0s and 1s. Write a program to find and output the K-th string according to the dictionary order. If s​uch a string doesn’t exist, or the input is not valid, please output “Impossible”. For example, if we have two ‘0’s and two ‘1’s, we will have a set with 6 different strings, {0011, 0101, 0110, 1001, 1010, 1100}, and the 4th string is 1001.


Input

The first line of the input file contains a single integer t (1 ≤ t ≤ 10000), the number of test cases, followed by the input data for each test case.
Each test case is 3 integers separated by blank space: N, M(2 <= N + M <= 33 and N , M >= 0), K(1 <= K <= 1000000000). N stands for the number of ‘0’s, M stands for the number of ‘1’s, and K stands for the K-th of string in the set that needs to be printed as output.

Output
For each case, print exactly one line. If the string exists, please print it, otherwise print “Impossible”. 

Sample In
3
2 2 2
2 2 7
4 7 47

Sample Out
0101
Impossible
01010111011


题目解析:

题意是所有n个0和m个1,然后组合排列,并按照从小到大的顺序排列,找到第k个二进制表示并输出。

好,我们可以组合一下排列,然后按照从小到大的十进制数据顺序来排列,然后查找第k个数据,最后转换为二进制输出。不过这样工作量挺大,我们组合排列的时候也要用二进制来排列。那么我们怎么去排列?先试着将题目中的例子,看看能不能找到什么规律。(接下来我们会看到,数学是很美妙的东西,等分析透彻了再动手写,代码会很简单)


排列组合我们写成Cmn。对于两个0,两个1来说:0011, 0101, 0110, 1001, 1010, 1100。

第一类:第一个数据为0011,最小,对应n个零+m个1;

第二类:第二个数据和第三个数据的时候,最高位的1增加,最低位的0降低,这时一个0一个1有两种组合(c21)

第三类:第四个到第六个数据,最高位1再提高一位至二进制的最高位,这时后面两个0和一个1有三种组合(c32)


同样我们可以通过两个0和三个1来组合成十个数:00111,01011,01101,01110,10011,10101,10110,11001,11010,11100

第一类:n个零+m个1——00111

第二类:最高位1进一位,后面m-1个1和1个0全排列——01011,01101,01110(c31)

第三类:最高位1再进一位,后面m-1个1和2个0全排列——10011,10101,10110,11001,11010,11100(c42)


通过这两个例子我们可以看到如下规律:

1+c(m-1+1)1+c(m-1+2)1+...+c(m-1+i)i+...+c(m-1+n)n

也就是最高位1不断提高,0的个数不断增加,组合数自然也不断增加。

通过展开组合数,第i个c(m-1+i)i和c(m-1+i-1)(i+1)的关系是:c(m-1+i+1)(i+1) = c(m-1+i)i * (m-1+i+1) / (i+1);


通过上述分析,不难看出,这里可以利用递归的形式,让1不断提高,直到所有的组合数超过k为止,必然k在c(m-1+i)i 中出现,那么可以确定n-i个0在最开始,紧接着是1,最后是i个零和m-1个1的组合!再递归的确定剩余的0和1的关系即可!但有一点需要注意,如果恰好1+...+c(m-1+i)i  == k;那么就可以确定i个零在最后,m-1个1在前面。


代码如下:

#include <stdio.h>
#include <stdlib.h>



int FindKthString(int arr[],int n,int m,int k);

int main(void)
{
    int n,m,k;

    while(1){
        printf("\nplease input n,m,k:");
        scanf("%d %d %d",&n,&m,&k);
        int *arr = (int *)malloc((n+m) * sizeof(int));
        int result = FindKthString(arr,n,m,k);
        if(!result)
            printf("impossible");
        else{
            for(int i = 0;i < n+m;++i)
                printf("%d",arr[i]);
        }
        free(arr);
    }
    return 0;
}

int FindKthString(int arr[],int n,int m,int k)
{
    int temp=1,sum=1;
    int i;

    if(k <= 0)      //传入k不合法
        return 0;

    if(k == 1){     //当k为第一个值的时候,直接返回说要结果:所有的1在末尾
        for(int i = 0;i < n+m;++i){ //n个零,m个一
            if(i<n)
                arr[i] = 0;
            else
                arr[i] = 1;
        }
        return 1;
    }
    for(i = 1;i <= n;++i){      //根据公式,求出递增的时候的和
        temp = temp*(m+i-1)/i;  //最高位1移动1位,就增加多少种数据情况。
        if(sum+temp >= k)       //必须加等号,证明i个0在末尾的时候查找成功。
            break;
        sum += temp;
    }
    if(i>n)
        return 0;
    //--i; 这句话不能要,必须在i个零里面才能匹配

    for(int j = 0;j < n+m; ++j){    //将数据分成n-i个0,接着是1,然后i个零,最后m-1个1
        if(j < n-i)
            arr[j] = 0;
        else if(j == n-i)
            arr[j] = 1;
        else if(j < n+1)
            arr[j] = 0;
        else
            arr[j] = 1;
    }
    arr = arr+n-i+1;        //调整指针,指向最高位1后面的地方
    if(sum+temp == k){      //如果正好为k,那么最末的i位全为0
        for(int j = 0;j < m+i-1;++j){
            if(j < m-1)
                arr[j] = 1;
            else
                arr[j] = 0;
        }
        return 1;
    }

    int result = FindKthString(arr,i,m-1,k-sum);    //递归进行求解
    if(!result)
        return 0;
    return 1;
}


总结:通过这个题可以看出来,数学是多么的美妙,那么复杂的问题,通过一个表达式就给解决了。代码也很简单!碰到问题,尽量导出表达式。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对您的问题,我可以提供以下思路: 1. 生成100W条随机字符串 由于有规律格式,我们可以考虑使用循环生成字符串。假设规律格式为:字母+数字+字母+数字,共8位。 ``` List<String> list = new ArrayList<>(); Random random = new Random(); for (int i = 0; i < 1000000; i++) { StringBuilder sb = new StringBuilder(); for (int j = 0; j < 8; j++) { if (j % 2 == 0) { // 字母 char c = (char) (random.nextInt(26) + 'A'); sb.append(c); } else { // 数字 int num = random.nextInt(10); sb.append(num); } } list.add(sb.toString()); } ``` 2. 随机获取100个不重复的字符串 使用 Collections.shuffle 方法打乱顺序,然后取100个即可。 ``` Collections.shuffle(list); List<String> randomList = list.subList(0, 100); ``` 3. 排序并输出到文件 将随机取得的100个字符串排序,然后输出到文件中。 ``` Collections.sort(randomList); File file = new File("output.txt"); try (PrintWriter writer = new PrintWriter(file)) { for (String str : randomList) { writer.println(str); } } ``` 完整代码如下: ``` import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; public class Test { public static void main(String[] args) throws Exception { List<String> list = new ArrayList<>(); Random random = new Random(); for (int i = 0; i < 1000000; i++) { StringBuilder sb = new StringBuilder(); for (int j = 0; j < 8; j++) { if (j % 2 == 0) { // 字母 char c = (char) (random.nextInt(26) + 'A'); sb.append(c); } else { // 数字 int num = random.nextInt(10); sb.append(num); } } list.add(sb.toString()); } Collections.shuffle(list); List<String> randomList = list.subList(0, 100); Collections.sort(randomList); File file = new File("output.txt"); try (PrintWriter writer = new PrintWriter(file)) { for (String str : randomList) { writer.println(str); } } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值