C#获取数组/字符串的k个字符的全部组合

   static IEnumerable<string> foo(string metachars, int i)
        {
            var query = metachars.Select(x => x.ToString().AsEnumerable());
            while (query.First().Count() < i)
            {
                var ee = query.First();
                //是原始的
                //query = query.SelectMany(x => metachars.Where(y => y > x.Last()).Select(y => x.Concat(y.ToString().AsEnumerable())));

                query = query.SelectMany(x => metachars.Where((y, y_index) => y_index > metachars.IndexOf(x.Last())).Select(y => x.Concat(y.ToString().AsEnumerable())));
                

                //无论哪种写法,都无法应用于内部含有重复字符串,因为对于上方的,char相同,会忽略掉重复的那个;下方那个返回第一个index,会出现重复
            }
                
            return query.Select(x => string.Join(",", x));
        }
         foreach (var item in foo("bacdef", 4))//使用
            {
                Console.WriteLine(item);
            }

 第一行按照char大小来排的是网络大神写的

第二行按照索引是我改进的另一种写法

 对于为什么要加AsEnumerable(),是因为只有可遍历的迭代器,才可以使用Concat方法

 

上方公式的原理就是:

 首先第一次,query可迭代的每个里面只含有一个字符,无论取First()还是第二、第三个,他们的容量count都是1

然后向后找,就是我们基本for循环来做的思想,一个个向后,如:abcd这样我们就是ab ac ad bc bd cd 这样一个个固定前方索引,向后找。此次count都是2

若输入为"abcd",3的话

第一次循环后query变为:ab ac ad bc bd cd

第二次过程则为:ab,因为b的索引为1,依次找比b大的,则abc abd, 对于ac ad..也是这样,对于cd,找比d大的没有,则就找不到啦

 

他的整个思想为,我要取数组的k个值的全部组合,那么我一点点从取一个开始(当然就是本身),然后所有取2个的组合,然后在2个的基础上取所有3个的组合,以此类推,直到k个!

 

对于数组也是一样,只需要在Last()后加.ToString(),对于int类型数组,也是只需要将Last()后转为int类型即可

 

   static IEnumerable<string> foo2(List<string> metachars, int i)
        {
            var query = metachars.Select(x => x.ToString().AsEnumerable());
            while (query.First().Count() < i)
            {
                var ee = query.First();
                //是原始的
                //query = query.SelectMany(x => metachars.Where(y => y > x.Last()).Select(y => x.Concat(y.ToString().AsEnumerable())));

                query = query.SelectMany(x => metachars.Where((y, y_index) => y_index > metachars.IndexOf(x.Last().ToString())).Select(y => x.Concat(y.ToString().AsEnumerable())));

                //无论哪种写法,都无法应用于内部含有重复字符串,因为对于上方的,char相同,会忽略掉重复的那个;下方那个返回第一个index,会出现重复
            }

            return query.Select(x => string.Join(",", x));
        }
        List<string> list2 = new List<string>() { "a","b","c","d","e","f"};//使用

            foreach (var item in foo2(list2, 3))
            {
                Console.WriteLine(item);
            }

如果想要int的话,就先使用string,然后再转成int

 使用List

 public static List<string> getAllCombination(int k, List<int> list)
        {
            var query = list.Select(x => x.ToString().AsEnumerable());
            while (query.First().Count() < k)
            {
                query = query.SelectMany(x => list.Where((y, y_index) => y_index >list.FindIndex(z=>z==int.Parse(x.Last().ToString()))).Select(y=>x.Concat(y.ToString())));
            }
            return query.Select(x => string.Join(",", x)).ToList();
        }

 以上的代码,看似没啥问题,但是当我数组中含有2位以上的数的时候,那就完蛋了,全乱套了,为了解决这个问题,我研究出如下方法!

 

通用非重复数组取数字方法!!!!By 程序杰杰

//适用于所有的非重复的数组方法!!! 

//由博主本人研究写出,如需引用必须注明博主网址哦

  public static List<List<int>> getAllCombinationCeshi2(int k, List<int> list)
        {
            List<List<int>> list1 = new List<List<int>>();//用来记录,选取k个元素的全部组合
            list.ForEach(x => { list1.Add(new List<int>() { x }); });

            List<string> listCopy = new List<string>();
            list.ForEach(x=> listCopy.Add(x.ToString()));
            while (list1[0].Count < k)
            {
                List<List<int>> list2 = new List<List<int>>();
                //select内部只能是一个个的,将这些组合成一个序列
                //selectMany内部必须要是可迭代的序列,将序列合并
                //选择比当前索引大的全部组合
                listCopy = listCopy.SelectMany((x,x_index)=> list.Where((y, y_index) => y_index > list.FindIndex(z => z == list1[x_index][list1[x_index].Count-1])).Select(y=> x+","+y)).ToList();
                //更新list1
                foreach (var item in listCopy)
                {
                    list2.Add(new List<int>());//此时一定是最后的一个list,因为是新加的嘛
                    string[] strs=item.Split(',');
                    foreach (var item1 in strs)
                    {
                        list2[list2.Count - 1].Add(int.Parse(item1));
                    }

                }
                list1 = list2;
            }
            return list1;

        }

 

Select与SelectMany

        //测试
            List<int> listCeshi = new List<int>() { 10,2,30,4,5};
            IEnumerable<int> ceshi01 = listCeshi.Select(x=>x);//select内部lambda是一个个的值,将这些值合并为一个新的序列
            IEnumerable<int> ceshi02 = listCeshi.SelectMany(x => listCeshi.Select(y => y));//selectMany内部lambda是一个序列,它将序列合并为一个序列

=====2020=====

java实现

//随机取字符串k个的全部排列(目前写字符无重复)
public class RandomFetchChar {
    public static void main(String[] args) {
        //System.out.println("abv".substring(3,3));
        System.out.println(getAllPermutation("abc",4));

    }
    static String front="";
    static String rear="";
    static ArrayList<String> temp=null;
    public static ArrayList<String> getAllPermutation(String str, int k){
        if(str.length()<k){
            System.out.println("输入有误");
            return null;
        }
        ArrayList<String> res=new ArrayList<String>();
        if(k==1){
            for (int i = 0; i < str.length(); i++) {
                res.add(""+str.charAt(i));
            }
        }else {
            for (int i = 0; i < str.length(); i++) {
                front=str.substring(0,i);
                rear=str.substring(i+1,str.length());
                temp=getAllPermutation(front+rear,k-1);
                for (int j = 0; j < temp.size(); j++) {
                    temp.set(j,str.charAt(i)+temp.get(j));
                }
                res.addAll(temp);

            }
        }
        return res;
    }
}

 //泛型数组实现

 private <T> LinkedList<LinkedList<T>> getKCombine(T arr[],int k){
        LinkedList<T> linkedList=new LinkedList<>();
        for (int i = 0; i < arr.length; i++) {
            linkedList.add(arr[i]);
        }
        LinkedList<LinkedList<T>> assistList=new LinkedList<>();
        LinkedList<LinkedList<T>> res=getAllCombine(linkedList,arr.length,k,assistList);
        return res;
    }

    private <T> LinkedList<LinkedList<T>> getAllCombine(LinkedList<T> linkedList,int len,int k,LinkedList<LinkedList<T>> assistList){
        if(len<k){
            throw new RuntimeException("k值输入过大");
        }
        LinkedList<LinkedList<T>> res=new LinkedList<>();
        LinkedList<T> temp;
        if(k==1){
            for (int i = 0; i < linkedList.size(); i++) {
                temp=new LinkedList<>();
                temp.add(linkedList.get(i));
                res.add(temp);
            }
            return res;
        }
        for (int i = 0; i < linkedList.size(); i++) {
            T first=linkedList.get(i);
            linkedList.remove(i);//移除
            assistList=getAllCombine(linkedList,len-1,k-1,assistList);
            linkedList.add(i,first);//执行完再加回来
            for(LinkedList<T> item:assistList){
                item.addFirst(first);
            }
            res.addAll(assistList);
        }
        return res;
    }

 

posted @ 2020-05-08 10:34  程序杰杰  阅读(1090)  评论(0编辑  收藏  举报