从二进制字符串中查找可能的对

brgchamk  于 2021-08-25  发布在  Java
关注(0)|答案(5)|浏览(426)

我有一个字符串数组,值只有0和1。
示例:[“100”、“110”、“010”、“011”、“100”]
现在,我想将每个字符串与所有其他字符串进行比较,看看其中有多少字符串相差1。例如:
“100”与“110”相比,检查每个索引处的元素,如果它们最多相差1个位置,则认为它们相似。因此,对于上面数组中的字符串“100”,类似的元素是“110”和“100”。
我的程序返回一个整数数组,其中每个值表示列表中该索引处类似字符串的数量。
例子:
输入:
["100" , "110", "010", "011", "100"]
输出:[2,3,2,1,2]
说明:在索引1处,输入为“110”,类似的字符串为3,分别为“100”、“010”、“100”。
这是我的密码:

  1. static List<Integer> process(String[] input) {
  2. List<Integer> list = new ArrayList<>();
  3. for (int i = 0; i < input.length; i++) {
  4. int count = 0;
  5. for (int j = 0; j < input.length; j++) {
  6. if (i != j) {
  7. int differ = 0;
  8. for (int k = 0; k < input[i].length(); k++) {
  9. if (input[i].charAt(k) != input[j].charAt(k)) {
  10. differ++;
  11. }
  12. if (differ > 1) {
  13. break;
  14. }
  15. }
  16. if (differ <= 1) {
  17. count++;
  18. }
  19. }
  20. }
  21. list.add(count);
  22. }
  23. return list;
  24. }

如何提高此代码的时间复杂度?
所有字符串的长度相同,长度范围为1到20。输入数组的大小最多为10000。

ncecgwcz

ncecgwcz1#

我们可以利用所有输入字符串长度相同的事实,在字符0和1上构建一个简单的二进制基数树,并将匹配字符串的计数存储在叶子中。
然后,您可以使用递归函数为每个输入字符串导航树,最多考虑一个发散点,即访问具有相反字符的子级。您可以对以这种方式到达的所有叶节点求和。
首先,我们构建树:

  1. static class Node
  2. {
  3. Node[] child = new Node[2];
  4. int count;
  5. }
  6. static Node buildTree(String[] s)
  7. {
  8. Node root = new Node();
  9. for(String bs : s)
  10. {
  11. Node node = root;
  12. for(char c : bs.toCharArray())
  13. {
  14. int i = c - '0';
  15. if(node.child[i] == null)
  16. node.child[i] = new Node();
  17. node = node.child[i];
  18. }
  19. node.count++;
  20. }
  21. return root;
  22. }

然后我们使用递归来计算匹配的字符串。注意,如果我们到达叶子和 diff 是0,也就是说,我们找到了原始字符串,然后我们需要将计数减少1,因为我们不计算自匹配。

  1. static int countDiff(Node node, String s, int pos, int diff)
  2. {
  3. if(pos == s.length())
  4. return diff == 0 ? node.count-1 : node.count;
  5. int i = s.charAt(pos) - '0';
  6. int sum = 0;
  7. if(diff == 0 && node.child[(i+1)%2] != null)
  8. sum += countDiff(node.child[(i+1)%2], s, pos+1, 1);
  9. if(node.child[i] != null)
  10. sum += countDiff(node.child[i], s, pos+1, diff);
  11. return sum;
  12. }

测试:

  1. String[] s = {"100" , "110", "010", "011", "100"};
  2. Node root = buildTree(s);
  3. int[] count = new int[s.length];
  4. for(int i=0; i<s.length; i++)
  5. count[i] = countDiff(root, s[i], 0, 0);
  6. System.out.println(Arrays.toString(count));

输出:

  1. [2, 3, 2, 1, 2]
展开查看全部
6xfqseft

6xfqseft2#

你可以使用set。
把所有的字符串放在一起 s 对于每个字符串,生成因单个更改而不同的所有字符串
计数属于的已生成字符串数 s 它将具有复杂性 O(n*m*m) 而不是 O(n*n*m) 你的算法已经成功了。 n 是字符串和的数目 m 是字符串的长度。
因此,这是更好的方面 n ,但更糟糕的是 m .
还需要 O(n*m) 记忆。原始算法需要 O(max(log(n), log(m))) 记忆。
为了获得更好的性能,请将字符串转换为整数。

inb24sb2

inb24sb23#

我真的很喜欢拉弗尔的答案,它使用了根树。
但作为一种更快实现的替代方案,您可以使用 int[] 作为频率Map:索引 i 存储数字的多少份副本 iinput .
频率Map数组的大小取决于输入字符串的宽度-因此它取决于 int[220] == int[1'048'576] .

  1. static List<Integer> process(String[] input) {
  2. var bitWidth = input[0].length();
  3. var values = new int[input.length];
  4. var freqMap = new int[1 << bitWidth]; // 2^bitWidth;
  5. // fill values and frequency map
  6. for (var i = 0; i < values.length; i++) {
  7. var value = toInt(input[i]);
  8. values[i] = value;
  9. freqMap[value]++;
  10. }
  11. var result = new ArrayList<Integer>(values.length);
  12. // fill results
  13. for (var value : values) {
  14. var numClose = freqMap[value] - 1;
  15. // close value: one bit flipped
  16. for (var iBit = 0; iBit < bitWidth; iBit++) {
  17. var closeVal = flipBit(value, iBit);
  18. numClose += freqMap[closeVal];
  19. }
  20. result.add(numClose);
  21. }
  22. return result;
  23. }
  24. private static int toInt(String inputValue) {
  25. var result = 0;
  26. for (int pos = inputValue.length() - 1, posWeight = 1; pos >= 0; pos--, posWeight <<= 1) {
  27. if (inputValue.charAt(pos) == '1') {
  28. result += posWeight;
  29. }
  30. }
  31. return result;
  32. }
  33. private static int flipBit(int value, int bitPosition) {
  34. return value ^ (1 << bitPosition);
  35. }
展开查看全部
hmae6n7t

hmae6n7t4#

弗兰肯斯坦的答案来自:
使用 Set (实际上 HashMap 支持重复输入数字),如talex的答案中所示
使用 Integer.valueOf(String, 2) 将输入转换为 int 史莱恩斯大师的答案中的数字
使用位翻转计算闭合值,如用户16394029的答案所示
我们得到的代码是:

  1. static List<Integer> process(String[] input) {
  2. var values = new ArrayList<Integer>(input.length);
  3. var freqMap = new HashMap<Integer, Integer>(input.length);
  4. var bitWidth = input[0].length();
  5. for (var inputValue : input) {
  6. var value = Integer.parseInt(inputValue, 2);
  7. values.add(value);
  8. freqMap.compute(value, (key, old) -> (old == null) ? 1 : (old + 1));
  9. }
  10. for (var i = 0; i < values.size(); i++) {
  11. var value = values.get(i);
  12. var numClose = freqMap.get(value) - 1;
  13. for (var iBit = 0; iBit < bitWidth; iBit++) {
  14. numClose += freqMap.getOrDefault(flipBit(value, iBit), 0);
  15. }
  16. values.set(i, numClose);
  17. }
  18. return values;
  19. }
  20. private static int flipBit(int value, int bitPosition) {
  21. return value ^ (1 << bitPosition);
  22. }
展开查看全部
t40tm48m

t40tm48m5#

我可以提出一个建议 O(N^2) 方法使用 bitwise XOR . 如果两位不同,则异或的结果为1。因此,我们可以使用这个属性来找到位不同的位置的计数。

  1. int[] num = new int[input.length];
  2. for (int i = 0; i < input.length; i++)
  3. num[i] = Integer.valueOf(input[i], 2); // converting to integral form
  4. for (int i = 0; i < input.length; i++)
  5. {
  6. int count = 0;
  7. for (int j = 0; j < input.length; j++)
  8. {
  9. if (i == j) // skip
  10. continue;
  11. if (Integer.bitCount(num[i] ^ num[j]) <= 1) // similar
  12. {
  13. ++count;
  14. }
  15. }
  16. list.add(count);
  17. }

我用过 bitCount 方法 java.lang package 它返回一个数的二元补码二进制表示中的一位数的计数,以查找结果中的设置位 XOR . 结果中的设置位数 XOR 表示两个数字之间的许多位置不同。

展开查看全部

相关问题