Are there any better methods to do permutation of string?

前端 未结 20 1348
醉话见心
醉话见心 2020-11-27 11:15
void permute(string elems, int mid, int end)
{
    static int count;
    if (mid == end) {
        cout << ++count << \" : \" << elems << end         


        
相关标签:
20条回答
  • 2020-11-27 11:36

    Even I found it difficult to understand that recursive version of the first time and it took me some time to search for a berre way.Better method to find (that I can think of) is to use the algorithm proposed by Narayana Pandita. The basic idea is:

    1. First sort the given string in no-decreasing order and then find the index of the first element from the end that is less than its next character lexicigraphically. Call this element index the 'firstIndex'.
    2. Now find the smallest character which is greater thn the element at the 'firstIndex'. Call this element index the 'ceilIndex'.
    3. Now swap the elements at 'firstIndex' and 'ceilIndex'.
    4. Reverse the part of the string starting from index 'firstIndex+1' to the end of the string.
    5. (Instead of point 4) You can also sort the part of the string from index 'firstIndex+1' to the end of the string.

    Point 4 and 5 do the same thing but the time complexity in case of point 4 is O(n*n!) and that in case of point 5 is O(n^2*n!).

    The above algorithm can even be applied to the case when we have duplicate characters in the string. :

    The code for displaying all the permutation of a string :

    #include <iostream>
    
    using namespace std;
    
    void swap(char *a, char *b)
    {
        char tmp = *a;
        *a = *b;
        *b = tmp;
    }
    
    
    int partition(char arr[], int start, int end)
    {
        int x = arr[end];
        int i = start - 1;
        for(int j = start; j <= end-1; j++)
        {
            if(arr[j] <= x)
            {
                i = i + 1;
                swap(&arr[i], &arr[j]);
            }
        }
        swap(&arr[i+1], &arr[end]);
        return i+1;
    }
    
    void quickSort(char arr[], int start, int end)
    {
        if(start<end)
        {
            int q = partition(arr, start, end);
            quickSort(arr, start, q-1);
            quickSort(arr, q+1, end);
        }
    }
    
    int findCeilIndex(char *str, int firstIndex, int n)
    {
        int ceilIndex;
        ceilIndex = firstIndex+1;
    
        for (int i = ceilIndex+1; i < n; i++)
        {
            if(str[i] >= str[firstIndex] && str[i] <= str[ceilIndex])
                ceilIndex = i;
        }
        return ceilIndex;
    }
    
    void reverse(char *str, int start, int end)
    {
        while(start<=end)
        {
            char tmp = str[start];
            str[start] = str[end];
            str[end] = tmp;
            start++;
            end--;
        }
    }
    
    void permutate(char *str, int n)
    {
        quickSort(str, 0, n-1);
        cout << str << endl;
        bool done = false;
        while(!done)
        {
            int firstIndex;
            for(firstIndex = n-2; firstIndex >=0; firstIndex--)
            {
                if(str[firstIndex] < str[firstIndex+1])
                    break;
            }
            if(firstIndex<0)
                done = true;
            if(!done)
            {
                int ceilIndex;
                ceilIndex = findCeilIndex(str, firstIndex, n);
                swap(&str[firstIndex], &str[ceilIndex]);
                reverse(str, firstIndex+1, n-1);
                cout << str << endl;
            }
        }
    }
    
    
    int main()
    {
        char str[] = "mmd";
        permutate(str, 3);
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 11:37

    I've written a permutation algorithm recently. It uses a vector of type T (template) instead of a string, and it's not super-fast because it uses recursion and there's a lot of copying. But perhaps you can draw some inspiration for the code. You can find the code here.

    0 讨论(0)
  • 2020-11-27 11:38

    Any algorithm that makes use of or generates all permutations will take O(N!*N) time, O(N!) at the least to generate all permutations and O(N) to use the result, and that's really slow. Note that printing the string is also O(N) afaik.

    In a second you can realistically only handle strings up to a maximum of 10 or 11 characters, no matter what method you use. Since 11!*11 = 439084800 iterations (doing this many in a second on most machines is pushing it) and 12!*12 = 5748019200 iterations. So even the fastest implementation would take about 30 to 60 seconds on 12 characters.

    Factorial just grows too fast for you to hope to gain anything by writing a faster implementation, you'd at most gain one character. So I'd suggest Prasoon's recommendation. It's easy to code and it's quite fast. Though sticking with your code is completely fine as well.

    I'd just recommend that you take care that you don't inadvertantly have extra characters in your string such as the null character. Since that will make your code a factor of N slower.

    0 讨论(0)
  • 2020-11-27 11:38

    Do you want to run through all the permutations, or count the number of permutations?

    For the former, use std::next_permutation as suggested by others. Each permutation takes O(N) time (but less amortized time) and no memory except its callframe, vs O(N) time and O(N) memory for your recursive function. The whole process is O(N!) and you can't do better than this, as others said, because you can't get more than O(X) results from a program in less than O(X) time! Without a quantum computer, anyway.

    For the latter, you just need to know how many unique elements are in the string.

    big_int count_permutations( string s ) {
        big_int divisor = 1;
        sort( s.begin(), s.end() );
        for ( string::iterator pen = s.begin(); pen != s.end(); ) {
            size_t cnt = 0;
            char value = * pen;
            while ( pen != s.end() && * pen == value ) ++ cnt, ++ pen;
            divisor *= big_int::factorial( cnt );
        }
        return big_int::factorial( s.size() ) / divisor;
    }
    

    Speed is bounded by the operation of finding duplicate elements, which for chars can be done in O(N) time with a lookup table.

    0 讨论(0)
  • 2020-11-27 11:40

    Why dont you try std::next_permutation() or std::prev_permutation() ?

    Links:

    std::next_permutation()
    std::prev_permutation()

    A simple example:

    #include<string>
    #include<iostream>
    #include<algorithm>
    
    int main()
    {
       std::string s="123";
       do
       {
    
          std::cout<<s<<std::endl;
    
       }while(std::next_permutation(s.begin(),s.end()));
    }
    

    Output:

    123
    132
    213
    231
    312
    321
    
    0 讨论(0)
  • 2020-11-27 11:42

    I'd like to second Permaquid's answer. The algorithm he cites works in a fundamentally different way from the various permutation enumeration algorithms that have been offered. It doesn't generate all of the permutations of n objects, it generates a distinct specific permutation, given an integer between 0 and n!-1. If you need only a specific permutation, it's much faster than enumerating them all and then selecting one.

    Even if you do need all permutations, it provides options that a single permutation enumeration algorithm does not. I once wrote a brute-force cryptarithm cracker, that tried every possible assignment of letters to digits. For base-10 problems, it was adequate, since there are only 10! permutations to try. But for base-11 problems took a couple of minutes and base-12 problems took nearly an hour.

    I replaced the permutation enumeration algorithm that I had been using with a simple i=0--to--N-1 for-loop, using the algorithm Permaquid cited. The result was only slightly slower. But then I split the integer range in quarters, and ran four for-loops simultaneously, each in a separate thread. On my quad-core processor, the resulting program ran nearly four times as fast.

    Just as finding an individual permutation using the permutation enumeration algorithms is difficult, generating delineated subsets of the set of all permutations is also difficult. The algorithm that Permaquid cited makes both of these very easy

    0 讨论(0)
提交回复
热议问题