LeetCode algorithm problem 117 solution + joint search set

Posted by kennethl on Sun, 20 Feb 2022 20:15:38 +0100

1, Topic retelling

leetCode algorithm Title:
https://leetcode-cn.com/problems/H6lPxb/
Briefly described as:
1. Provides an array of strings. There are letter ectopic words between the string elements of the array. For example, "aaabb" and "baaab"; That is, the letters that make up the string are the same, and the letters appear the same number of times. The letter position can be different.
2. Define that two strings are similar: if a string can change into another string after exchanging the position of letters at most once, the two strings are similar. For example, "ab" and "ba"; "wbw" and "bww"; "stars" and "tsars";
3. Given a string list strs. Meet condition 1; How many similar string groups are there in strs?

2, Own solution

The first thought is to write a method to judge whether the two strings are similar. It is relatively simple and directly give the code:

	@Test
    public void test() {
        System.out.println(isSimilarString("tars", "rats"));
        System.out.println(isSimilarString("tars", "star"));
    }

    private boolean isSimilarString(String str1, String str2) {
        if (str1.length() != str2.length()) {
            return false;
        }
        List<Integer> diffIndexList = new ArrayList<>();
        for (int i = 0; i < str1.length(); i++) {
            if (str1.charAt(i) != str2.charAt(i)) {
                diffIndexList.add(i);
                if (diffIndexList.size() > 2) {
                    return false;
                }
            }
        }
        if (diffIndexList.size() == 0) {
            return true;
        }
        if (diffIndexList.size() == 2) {
            return str1.charAt(diffIndexList.get(0)) == str2.charAt(diffIndexList.get(1)) && str1.charAt(diffIndexList.get(1)) == str2.charAt(diffIndexList.get(0));
        }
        return false;
    }

The following code gives you ideas first. You can try:
Traverse the string array. First, compare Str0 with Str1. If they are similar, put the two strings into one set. If they are not similar, the two strings are put into a set. Str2 is compared with the previous set in turn. If the elements of the set are similar to Str2, add Str2 to the existing set. Otherwise, Str2 creates its own set.

public int numSimilarGroups(String[] strs) {
        List<List<String>> group = new ArrayList<>();
        for (int i = 0; i < strs.length; i++) {
            String str = strs[i];
            boolean addFlag = false;
            groupLoop:
            for (List<String> list : group) {
                for (String s : list) {
                    if (isSimilarString(s, str)) {
                        list.add(str);
                        addFlag = true;
                        break groupLoop;
                    }
                }
            }
            if (!addFlag) {
                List<String> listObj = new ArrayList<>();
                listObj.add(str);
                group.add(listObj);
            }
        }
        return group.size();
    }

Then, after a simple test, it is submitted, and the result is reported as error. The error prompt cases are:

Think for A long time. When you don't have ideas, go back to read the question repeatedly and understand the question: "tars" and "rates" are similar (exchange the positions of 0 and 2); "rats" and "arts" are also similar, but "star" is not similar to "stars", which is simplified to A and B, B and C are similar, but A and C are not similar. This is different from the similarity in mathematics. Therefore, when traversing characters, it is possible that A, C, B, A and C are not similar. Two sets are created, but B is similar to A and B is similar to C. in this way, when traversing to B, the two sets of A and C need to be combined into A similar set. In fact, we can understand similarity as connectivity, which is in line with our thinking:

So modify your own code:

	public int numSimilarGroups(String[] strs) {
        List<List<String>> group = new ArrayList<>();
        for (int i = 0; i < strs.length; i++) {
            String str = strs[i];
            List<Integer> similarListIndex = new ArrayList<>();
            groupLoop:
            for (int j = 0; j < group.size(); j++) {
                List<String> list = group.get(j);
                for (String s : list) {
                    if (isSimilarString(s, str)) {
                        similarListIndex.add(j);
                        break;
                    }
                }
            }
            if (similarListIndex.isEmpty()) {
                List<String> listObj = new ArrayList<>();
                listObj.add(str);
                group.add(listObj);
            } else if(similarListIndex.size() == 1) {
                group.get(similarListIndex.get(0)).add(str);
            } else {
                List<String> mergeList = new ArrayList<>();
                mergeList.add(str);
                for (Integer listIndex : similarListIndex) {
                    mergeList.addAll(group.get(listIndex));
                }
                int modify = 0;
                for (int listIndex : similarListIndex) {
                    group.remove(listIndex - modify);
                    modify++;
                }
                group.add(mergeList);
            }
        }
        return group.size();
    }

Comprehensive submission Code: https://leetcode-cn.com/problems/H6lPxb/

3, Code learning - joint search set

After that, I looked at the official explanation. There was not much explanation. I read it for an hour and understood it a little. The explanation of the supplementary code is as follows. By the way, I also learned new knowledge - and search sets.
Someone else's code:

class Solution {
    public int numSimilarGroups(String[] strs) {
        int n = strs.length;
        int cnt = n;
		// And the initialization of the search set. At the beginning, the father or ancestor of each element is itself.
        int[] fathers = new int[n];
        for(int i = 0; i < n; ++i){
            fathers[i] = i;
        }
		// At first, the default grouping is equal to the length of the array, and each element is a group
		// Traversal uses the method of combining numbers. Every two elements are combined once to judge whether they are similar.
        for(int i = 0; i < n; ++i){
            for(int j = i+1; j < n; ++j){
				// The following elements are similar to the previous elements. The simple idea is that the two elements can be put into a group. The number of groups can be reduced by one. However:
				// ***Difficulties*** 
				// Then, judge whether similar elements have a common ancestor,
				// If the ancestors are the same, for example, a is similar to B, a is the ancestor of B; A is similar to C, a is the ancestor of C; A has pulled B and C into a group. When traversing to B and C, the ancestors are all a, so there is no need to subtract one. The effect of their number of groups has been documented.
				// If your ancestors are different, you can subtract one.
					//Situation 1: primitive transformation, ancestors are themselves.
					//Case 2: consolidation of different groups. For example, a is similar to B, a is not similar to C, and B is similar to C; B's ancestor is a and C's ancestor is C. It's different. Add C to the descendants of A. Reduce the number of groups by one. 
                if(isSimilar(strs[i], strs[j]) && union(fathers, i, j)){
                    cnt--;
                }
            }
        }
        return cnt;
    }

    public boolean isSimilar(String s1, String s2){
        int cnt = 0;
        for(int i = 0; i < s1.length(); ++i){
            if(s1.charAt(i) != s2.charAt(i)) cnt++;
        }
        return cnt<=2;
    }

    public boolean union(int[] fathers, int i, int j){
        int a = findFather(fathers, i);
        int b = findFather(fathers, j);
        if(a != b){
            fathers[a] = b; // Where fathers[b] = a; It's OK. The characters are similar. Anyone can be an ancestor, as long as they remain unified and let the elements compared later be descendants.
            return true;
        }
        return false;
    }

    public int findFather(int[] fathers, int i){
        if(fathers[i] != i){
        	// Reference recursion solution.
            fathers[i] = findFather(fathers, fathers[i]);
        }
        return fathers[i];
    }
}

Author: SloanCheng
Link: https://leetcode-cn.com/problems/H6lPxb/solution/tu-bfshe-bing-cha-ji-liang-chong-fang-fa-hmof/
Source: LeetCode
All comments in the code are added by the author of this article.
The core idea of summarizing the algorithm is:
fathers[x] = y;
fathers[y] = y;
An array of fathers [] is added to record the relationship between the elements with subscript X and the elements with subscript Y.
When setting the array relationship, reference recursive solution.

Other considerations: such relationships can be used in business scenarios such as similarity, node connectivity, menu parent-child relationship, etc.
Analysis of advantages and disadvantages:
The referenced code is highly efficient and can directly calculate the results. It gives several groups of similar characters, but it is unknown which characters are in one group. In business scenarios, sometimes we may want to know which elements of a group have the same relationship. At this time, using my first method, you can know by looking at the group set.
And check the set and have richer knowledge, baidu on its own.

Topics: Java Algorithm leetcode