Full Arrangement of Strings

Posted by mazzzzz on Mon, 08 Jul 2019 01:06:50 +0200

Full permutation is very popular in written interview because it is moderately difficult. It can examine both recursive implementation and non-recursive implementation, so it is easy to distinguish the level of candidates. So in Baidu and Xunlei's campus recruitment and programmer and software designer's exams have been tested, so this paper summarizes the whole arrangement to help you better learn and understand. Any supplements to this article are welcome to be pointed out.
First of all, let's see how the title is required (Baidu Xunlei Recruitment Test).
I. Arrangement of Strings
Write a function in C++, such as Foo(const char *str), and print out the full arrangement of strs.
For example, the complete arrangement of abc: abc, acb, bca, dac, cab, cba

I. Recursive Realization of Full Arrangement

For convenience, use 123 as an example. There are 123, 132, 213, 231, 312 and 321 in the complete arrangement of 123. First, consider how 213 and 321 are derived. Obviously, these two numbers are obtained by exchanging 1 of 123 with the latter two numbers. Then we can exchange the second number of 123 and every three numbers to get 132. Similarly, 231 and 312 can be obtained from 213 and 321. So we can know that the full arrangement is to exchange each number with the number behind it from the first number. Once this rule is found, the recursive code is easy to write.

  1. #include<iostream>  
  2. using namespace std;  
  3. #include<assert.h>  
  4.   
  5. void Permutation(char* pStr, char* pBegin)  
  6. {  
  7.     assert(pStr && pBegin);  
  8.   
  9.     if(*pBegin == '\0')  
  10.         printf("%s\n",pStr);  
  11.     else  
  12.     {  
  13.         for(char* pCh = pBegin; *pCh != '\0'; pCh++)  
  14.         {  
  15.             swap(*pBegin,*pCh);  
  16.             Permutation(pStr, pBegin+1);  
  17.             swap(*pBegin,*pCh);  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. int main(void)  
  23. {  
  24.     char str[] = "abc";  
  25.     Permutation(str,str);  
  26.     return 0;  
  27. }  

Another way of writing:

  1. //k denotes the number currently selected, and m denotes the total number.  
  2. void Permutation(char* pStr,int k,int m)  
  3. {  
  4.     assert(pStr);  
  5.   
  6.     if(k == m)  
  7.     {  
  8.         static int num = 1;  //Local static variables, used to count the number of full permutations  
  9.         printf("The first%d Permutation\t%s\n",num++,pStr);  
  10.     }  
  11.     else  
  12.     {  
  13.         for(int i = k; i <= m; i++)  
  14.         {  
  15.             swap(*(pStr+k),*(pStr+i));  
  16.             Permutation(pStr, k + 1 , m);  
  17.             swap(*(pStr+k),*(pStr+i));  
  18.         }  
  19.     }  
  20. }  
  21.   
  22. int main(void)  
  23. {  
  24.     char str[] = "abc";  
  25.     Permutation(str , 0 , strlen(str)-1);  
  26.     return 0;  
  27. }  
If there are duplicate characters in the string, the above method will certainly not meet the requirements, so now we have to find a way to remove duplicate columns.
2. Recursive Realization of Removing Repeated Full Arrangement
Because the full arrangement is to exchange each number with the number behind it from the first number. Let's try to add a judgement that if a number is the same as the following number, the two numbers will not be exchanged. For example, 122, the first number exchanges 212 and 221 with the latter. Then the second number in 122 does not need to be exchanged with the third number, but for 212, the second number is different from the third number, and 221 is obtained after the exchange. It repeats 221 obtained by exchanging the first number from the third number in 122. So this method is not good.

In another way, for 122, the first number 1 is exchanged with the second number 2 to get 212, and then the first number 1 is exchanged with the third number 2. At this time, because the third number equals the second number, the first number is no longer exchanged with the third number. Considering 212 again, the exchange between the second number and the third number can be solved by 221. At this point, the full permutation is generated.
So we also get the rule of eliminating duplication in the whole permutation, that is, the complete permutation of eliminating duplication is the exchange of each number from the first number to the non-duplicated number after it. The complete code is given below:
  1. #include<iostream>  
  2. using namespace std;  
  3. #include<assert.h>  
  4.   
  5. //Are there any characters in the [nBegin, nEnd] interval equal to those labeled pEnd?  
  6. bool IsSwap(char* pBegin , char* pEnd)  
  7. {  
  8.     char *p;  
  9.     for(p = pBegin ; p < pEnd ; p++)  
  10.     {  
  11.         if(*p == *pEnd)  
  12.             return false;  
  13.     }  
  14.     return true;  
  15. }  
  16. void Permutation(char* pStr , char *pBegin)  
  17. {  
  18.     assert(pStr);  
  19.   
  20.     if(*pBegin == '\0')  
  21.     {  
  22.         static int num = 1;  //Local static variables, used to count the number of full permutations  
  23.         printf("The first%d Permutation\t%s\n",num++,pStr);  
  24.     }  
  25.     else  
  26.     {  
  27.         for(char *pCh = pBegin; *pCh != '\0'; pCh++)   //The number of pBegin s can be exchanged with the number behind it to get a new arrangement.  
  28.         {  
  29.             if(IsSwap(pBegin , pCh))  
  30.             {  
  31.                 swap(*pBegin , *pCh);  
  32.                 Permutation(pStr , pBegin + 1);  
  33.                 swap(*pBegin , *pCh);  
  34.             }  
  35.         }  
  36.     }  
  37. }  
  38.   
  39. int main(void)  
  40. {  
  41.     char str[] = "baa";  
  42.     Permutation(str , str);  
  43.     return 0;  
  44. }  
OK, so far we have been able to write recursive methods skillfully, and consider the duplicate sequence problems that may arise from duplicate data in strings. So how do you use non-recursive methods to get the full permutation?

3. Non-recursive Realization of Full Arrangement
To consider the non-recursive implementation of full permutation, first consider how to compute the next permutation of strings. For example, the next order of "1234" is "1243". As long as the next permutation of strings is found repeatedly, the whole permutation will be solved.
How do I calculate the next permutation of strings? Considering the string 926520, we look for the first pair of adjacent incremental numbers from the back to the front, and "20" and "52" are non-incremental. 26 satisfies the requirement. The former number 2 is called the replacement number, and the subscript of the replacement number is called the replacement point. Then we find the smallest number larger than the replacement number from the back (this number must exist), 0, 2. No, 5 can, 5 and 2 can be exchanged to "956220", and then the string "6220" after the replacement point can be inverted to "950226".
For the "4321" which is already the largest arrangement, the STL processing method is used to reverse the whole string to get the smallest arrangement "1234" and return false.

In this way, a non-recursive full permutation can be easily achieved by adding a loop and calculating the next permutation function of the string. algorithm . According to the above ideas and referring to the implementation source code in STL, it is not difficult to write a high quality code. It is worth noting that you can write your own code for quick sorting of strings before looping (see Classical Algorithms in Vernacular 6) Quick Sorting Quick Solution) can also be directly used in the VC library Quick Sorting Functions (see "Use Quick Sorting Functions in VC Library Functions"). The complete code is listed below:

  1. #include<iostream>  
  2. #include<algorithm>  
  3. #include<cstring>  
  4. using namespace std;  
  5. #include<assert.h>  
  6.   
  7. //Inversion interval  
  8. void Reverse(char* pBegin , char* pEnd)  
  9. {  
  10.     while(pBegin < pEnd)  
  11.         swap(*pBegin++ , *pEnd--);  
  12. }  
  13. //Next permutation  
  14. bool Next_permutation(char a[])  
  15. {  
  16.     assert(a);  
  17.     char *p , *q , *pFind;  
  18.     char *pEnd = a + strlen(a) - 1;  
  19.     if(a == pEnd)  
  20.         return false;  
  21.     p = pEnd;  
  22.     while(p != a)  
  23.     {  
  24.         q = p;  
  25.         p--;  
  26.         if(*p < *q)  //Find the adjacent 2 numbers in descending order, the former is the replacement number.  
  27.         {  
  28.              //Look back and forth for the first number larger than the substitution point  
  29.             pFind = pEnd;  
  30.             while(*pFind < *p)  
  31.                 --pFind;  
  32.             swap(*p , *pFind);  
  33.             //All reversals of the number after the replacement point  
  34.             Reverse(q , pEnd);  
  35.             return true;  
  36.         }  
  37.     }  
  38.     Reverse(a , pEnd);   //If there is no next permutation, all of them are reversed and returned to false __________.  
  39.     return false;  
  40. }  
  41.   
  42. int cmp(const void *a,const void *b)  
  43. {  
  44.     return int(*(char *)a - *(char *)b);  
  45. }  
  46. int main(void)  
  47. {  
  48.     char str[] = "bac";  
  49.     int num = 1;  
  50.     qsort(str , strlen(str),sizeof(char),cmp);  
  51.     do  
  52.     {  
  53.         printf("The first%d Permutation\t%s\n",num++,str);   
  54.     }while(Next_permutation(str));  
  55.     return 0;  
  56. }  
So far, we have used recursive and non-recursive methods to solve the problem of full permutation. To sum up, this is:
1. Full permutation means that each number is exchanged with the number behind it from the first number.
2. The complete arrangement of de-duplication is the exchange of each number from the first number to the non-repetitive number after it.
3. Fully arranged non-recursive is to find the replacement number and replacement point from back to front, and then to find the first number larger than the replacement number from back to front to exchange with the replacement number, and finally to reverse all the data after the replacement point.

2. Combination of strings

Title: Input a string and output all combinations of characters in the string. For example, if you enter a B c, its combination is a, b, c, ab, ac, bc, abc.

Above we discussed in detail how to use recursive thinking to find the arrangement of strings. Similarly, this topic can also use recursive thinking to find the combination of strings.

Suppose we want to find a combination of m characters in a string of length n. Let's first scan the first character of the string from scratch. For the first character, we have two choices: first, to put this character in combination, then we need to select m-1 characters in the remaining n-1 characters; second, not to put this character in combination, and then we need to select m characters in the remaining n-1 characters. Both options are easy to implement with recursion. Here is the reference code for this idea:
  1. #include<iostream>  
  2. #include<vector>  
  3. #include<cstring>  
  4. using namespace std;  
  5. #include<assert.h>  
  6.   
  7. void Combination(char *string ,int number,vector<char> &result);  
  8.   
  9. void Combination(char *string)  
  10. {  
  11.     assert(string != NULL);  
  12.     vector<char> result;  
  13.     int i , length = strlen(string);  
  14.     for(i = 1 ; i <= length ; ++i)  
  15.         Combination(string , i ,result);  
  16. }  
  17.   
  18. void Combination(char *string ,int number , vector<char> &result)  
  19. {  
  20.     assert(string != NULL);  
  21.     if(number == 0)  
  22.     {  
  23.         static int num = 1;  
  24.         printf("The first%d Individual combinations\t",num++);  
  25.   
  26.         vector<char>::iterator iter = result.begin();  
  27.         for( ; iter != result.end() ; ++iter)  
  28.             printf("%c",*iter);  
  29.         printf("\n");  
  30.         return ;  
  31.     }  
  32.     if(*string == '\0')  
  33.         return ;  
  34.     result.push_back(*string);  
  35.     Combination(string + 1 , number - 1 , result);  
  36.     result.pop_back();  
  37.     Combination(string + 1 , number , result);  
  38. }  
  39.   
  40. int main(void)  
  41. {  
  42.     char str[] = "abc";  
  43.     Combination(str);  
  44.     return 0;  
  45. }  

Since the combination can be a combination of one character, two characters... Until n characters are combined, so in the function void Combination(char* string), we need a for loop. In addition, we use a vector to store the characters that we choose to put into the combination.
Method 2: Combination is realized by bit operation.

  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. int a[] = {1,3,5,4,6};  
  5. char str[] = "abcde";  
  6.   
  7. void print_subset(int n , int s)  
  8. {  
  9.     printf("{");  
  10.     for(int i = 0 ; i < n ; ++i)  
  11.     {  
  12.         if( s&(1<<i) )         //Determine which bits in the binary system of s are 1, that is to say, to take a bit.  
  13.             printf("%c ",str[i]);   //Or a[i]  
  14.     }  
  15.     printf("}\n");  
  16. }  
  17.   
  18. void subset(int n)  
  19. {  
  20.     for(int i= 0 ; i < (1<<n) ; ++i)  
  21.     {  
  22.         print_subset(n,i);  
  23.     }  
  24. }  
  25.   
  26.   
  27.   
  28. int main(void)  
  29. {  
  30.     subset(5);  
  31.     return 0;  
  32. }  

String Full Arrangement Expansion--Eight Queens Problem
Title: Place eight queens on 8 x 8 chess so that they can't attack each other, that is, any two queens can't be in the same row, column or diagonal diagonal slant. Each black grid in the figure below represents a queen, which is a suitable placement method. How many pendulums are requested?


This is the famous Queen of Eight problem. Recursion is usually used to solve this problem, and recursion requires high programming ability. Therefore, many interviewers prefer this topic to examine the applicant's ability to analyze complex problems and to program.

Since any two of the eight queens can't be in the same line, it must be one line for each queen. So we can define an array ColumnIndex[8], in which the number I represents the queen's column number in line I. First we initialize the eight numbers of ColumnIndex with 0-7, and then we have to do the whole arrangement of the array ColumnIndex. Since we initialize the numbers in the array with different numbers, any two queens must have different columns. We just need to determine whether the eight queens corresponding to each arrangement are on the same diagonal slant, that is, the two subscripts I and j of the array, i-j==ColumnIndex[i]-Column[j] or j-i==ColumnIndex[i]-ColumnIndex[j].

For a detailed discussion of the arrangement, see the explanation above.
The next step is to write the code. After thinking clearly, coding is not a difficult thing. Here is a reference code:

  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. int g_number = 0;  
  5. void Permutation(int * , int  , int );  
  6. void Print(int * , int );  
  7.   
  8. void EightQueen( )  
  9. {  
  10.     const int queens = 8;  
  11.     int ColumnIndex[queens];  
  12.     for(int i = 0 ; i < queens ; ++i)  
  13.         ColumnIndex[i] = i;    //Initialization  
  14.     Permutation(ColumnIndex , queens , 0);  
  15. }  
  16.   
  17. bool Check(int ColumnIndex[] , int length)  
  18. {  
  19.     int i,j;  
  20.     for(i = 0 ; i < length; ++i)  
  21.     {  
  22.         for(j = i + 1 ; j < length; ++j)  
  23.         {  
  24.             if( i - j == ColumnIndex[i] - ColumnIndex[j] || j - i == ColumnIndex[i] - ColumnIndex[j])   //On the diagonal line  
  25.                 return false;  
  26.         }  
  27.     }  
  28.     return true;  
  29. }  
  30. void Permutation(int ColumnIndex[] , int length , int index)  
  31. {  
  32.     if(index == length)  
  33.     {  
  34.         if( Check(ColumnIndex , length) )   //Checkboard current status is checked for legitimacy  
  35.         {  
  36.             ++g_number;  
  37.             Print(ColumnIndex , length);  
  38.         }  
  39.     }  
  40.     else  
  41.     {  
  42.         for(int i = index ; i < length; ++i)   //Full permutation  
  43.         {  
  44.             swap(ColumnIndex[index] , ColumnIndex[i]);  
  45.             Permutation(ColumnIndex , length , index + 1);  
  46.             swap(ColumnIndex[index] , ColumnIndex[i]);  
  47.         }  
  48.     }  
  49. }  
  50.   
  51. void Print(int ColumnIndex[] , int length)  
  52. {  
  53.     printf("%d\n",g_number);  
  54.     for(int i = 0 ; i < length; ++i)  
  55.         printf("%d ",ColumnIndex[i]);  
  56.     printf("\n");  
  57. }  
  58.   
  59. int main(void)  
  60. {  
  61.     EightQueen();  
  62.     return 0;  
  63. }  
Reproduced: http://zhedahht.blog.163.co

Topic: Input two integers n and m, from the sequence 1,2,3...n arbitrarily take a few numbers, make the sum equal to m, require listing all combinations.

  1. #include <iostream>  
  2. #include <list>  
  3. using namespace std;  
  4. list<int> list1;  
  5. void find_factor(int sum,int n)  
  6. {  
  7.     //Recursive export  
  8.     if(n<=0||sum<=0)  
  9.         return;  
  10.     //Output Find Number  
  11.     if(sum==n)  
  12.     {  
  13.         list1.reverse();  
  14.         for(list<int>::iterator iter=list1.begin();iter!=list1.end();iter++)  
  15.             cout<<*iter<<"+";  
  16.         cout<<n<<endl;  
  17.         list1.reverse();  
  18.     }  
  19.     list1.push_front(n);  
  20.     find_factor(sum-n,n-1);//n put it in  
  21.     list1.pop_front();  
  22.     find_factor(sum,n-1);//n is not in it  
  23. }  
  24.   
  25. int main(void)  
  26. {  
  27.     int sum,n;  
  28.     cin>>sum>>n;  
  29.     cout<<"All possible sequences are as follows:"<<endl;  
  30.     find_factor(sum,n);  
  31.     return 0;  
  32. }  

Topics: Programming