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; }