Permutations of capitalization

本秂侑毒 提交于 2019-11-29 15:44:58

You can modify individual characters if you convert your string to an array of char. Something like this should do the trick...

public static List<string> Permute( string s )
{
  List<string> listPermutations = new List<string>();

  char[] array = s.ToLower().ToCharArray();
  int iterations = (1 << array.Length) - 1;

  for( int i = 0; i <= iterations; i++ )
  {
    for( int j = 0; j < array.Length; j++ )
    array[j] = (i & (1<<j)) != 0 
                  ? char.ToUpper( array[j] ) 
                  : char.ToLower( array[j] );
    listPermutations.Add( new string( array ) );
  }
  return listPermutations;
}

Keep in mind that while the accepted answer is the most straightforward way of capitalizing an arbitrary letter, if you are going to change the capitalization repeatedly on the same set of letters (e.g., 32 times in "happy" and growing exponentially for longer words), it will be more efficient to turn the string into a char[], set the appropriate letter(s), and construct the string from the array.

To "permute" your string (technically, this isn't permutation since you're not changing the order of anything, but I don't want to be seen as an an*l-retentive :-), I would use a recursive approach, which basically "permutes" the string minus the first character and attaches those to the upper and lower variations of that character.

Something like (in C, my C# is not really up to par so you'll have to convert it):

#include <stdio.h>
#include <string.h>

static void permute (char *prefix, char *str) {
    char *newPrefix;

    /* End of string, print and return. */

    if (*str == '\0') {
        printf ("%s\n", prefix);
        return;
    }

    /* Allocate space for new prefix. */

    if ((newPrefix = malloc (strlen (prefix) + 2)) == NULL) {
        printf ("ERROR: Cannot allocate memory.\n");
        return;
    }

    /* Do lowercase/sole version and upper version if needed. */

    sprintf (newPrefix, "%s%c", prefix, *str);
    permute (newPrefix, &(str[1]));

    if (islower (*str) {
        sprintf (newPrefix, "%s%c", prefix, toupper(*str));
        permute (newPrefix, &(str[1]));
    }

    /* Free prefix and return. */

    free (newPrefix);
}

 

int main (int argc, char *argv[]) {
    char *str, *strPtr;

    /* Check and get arguments. */

    if (argc < 2) {
        printf ("Usage: permute <string to permute>\n");
        return 1;
    }

    if ((str = malloc (strlen (argv[1]) + 1)) == NULL) {
        printf ("ERROR: Cannot allocate memory.\n");
        return 1;
    }
    strcpy (str, argv[1]);

    /* Convert to lowercase. */
    for (strPtr = s; *strPtr != '\0'; strPtr++)
        *strPtr = toupper (*strPtr);

    /* Start recursion with empty prefix. */

    permute ("", str);

    /* Free and exit. */

    free (str);
    return 0;
}

Running this as "permute Pax1" returns:

pax1
paX1
pAx1
pAX1
Pax1
PaX1
PAx1
PAX1

I was able to create a console app that does this..

 public static class Program
{
    static void Main()
    {
        Console.WriteLine("Enter string");
        string value = Console.ReadLine();

        value = value.ToLower();

        List<string> list = new List<string>();

         var results =
             from e in Enumerable.Range(0, 1 << value.Length)
             let p =
             from b in Enumerable.Range(0, value.Length)
             select (e & (1 << b)) == 0 ? (char?)null : value[b]
             select string.Join(string.Empty, p);

        foreach (string s in results)
        {
            string newValue = value;
            s.ToLower();
            foreach(char c in s)
            {
                var Old = c.ToString().ToLower();
                var New = c.ToString().ToUpper();
                newValue=ReplaceFirstOccurrence(newValue, Old, New);
            }
            list.Add(newValue);
        }

        foreach(string s in list)
        {
            Console.WriteLine(s);
        }

        Console.ReadKey();
    }

    public static string ReplaceFirstOccurrence(string Source, string Find, string Replace)
    {
        int Place = Source.IndexOf(Find);
        string result = Source.Remove(Place, Find.Length).Insert(Place, Replace);
        return result;
    }    
}

I got a list of all possible subsets of characters in the string then for each character in each subset, replaced them with their capital counterparts in the initial string, then made a list of those. Had to have a custom Replace function as the normal string.Replace would replace any occurrence of the character.

It's probably not the cleanest of code, but it does the job. This got brought up as a means of dynamically searching over encrypted fields, and I wanted to see how crazy that would really be..

Loosely speaking, something like below. I may have my ranges off by one, but the idea is sound.

def cap_n(in_str, pos):
  leading = in_str.substr(0, pos-1)
  trailing = in_str.substr(pos+1) # no second arg implies to end of string
  chr = in_str[pos].to_uppercase()
  return leading + chr + trailing

Use bitwise operations. For a word of length N you need an integer type represented by N bits. If the word is longer - split it. Iterate through values from 0 to 2N-1 and examine each bit from 0 to N-1. If the bit is 1 - upcase ( Char.ToUpper() ) the letter corresponding to that bit.

This approach is better that a recursive algorithm since it is not prone to stack overflows on long words.

Assuming:

1) You don't care too much that this is O(n*2^n)... although I'm curious to know: what is the best asymptotic run time for this type of problem?

2) Your input is all lowercase.

3) Your input is < 32 characters long. (# of usable bits in the permutation counter, i)

    List<string> permutate(string word)
{
    List<string> ret = new List<string>();

// MAGIC HAPPENS HERE
Dictionary<char,char> toUppers = new Dictionary<char,char>(26);
toUppers.Add('a', 'A');
toUppers.Add('b', 'B');
toUppers.Add('c', 'C');
toUppers.Add('d', 'D');
toUppers.Add('e', 'E');
toUppers.Add('f', 'F');
toUppers.Add('g', 'G');
toUppers.Add('h', 'H');
toUppers.Add('i', 'I');
toUppers.Add('j', 'J');
toUppers.Add('k', 'K');
toUppers.Add('l', 'L');
toUppers.Add('m', 'M');
toUppers.Add('n', 'N');
toUppers.Add('o', 'O');
toUppers.Add('p', 'P');
toUppers.Add('q', 'Q');
toUppers.Add('r', 'R');
toUppers.Add('s', 'S');
toUppers.Add('t', 'T');
toUppers.Add('u', 'U');
toUppers.Add('v', 'V');
toUppers.Add('w', 'W');
toUppers.Add('x', 'X');
toUppers.Add('y', 'Y');
toUppers.Add('z', 'Z');

char[] wordChars = word.ToCharArray();
int len = wordChars.Length;

// iterate the number of permutations
for(int i = 0; i < 2^len; i++) {
    char[] newWord = new char[len]();

    // apply "i" as a bitmask to each original char
    for(int n = 0; n < newWord.Length; n++) {
        if((1 << n) & i != 0) {
            newWord[n] = toUppers[wordChars[n]]; // or skip the dictionary and just call Char.ToUpper(wordChars[n])
        } else {
            newWord[n] = wordChars[n];
        }
    }
    ret.Add(new String(newWord));
}

    return ret;
}

Note: I haven't compiled or tested this code. This is also implementing the bitwise compare that sharptooth suggested above.

If order doesn't matter, you can try Linq. We enumerate all binary numbers in a range of [0..2**word.Length]. and treat each number as mask: 0 - lower case, 1 - upper case. FDor instance for happy we have

mask      string
----------------
00000  ->  happy
00001      Happy
00010      hAppy
00011      HAppy
00100      haPpy
00101      HaPpy
...
11111      HAPPY

Code:

  using System.Linq;

  ... 

  List<string> permutate(string word) =>
    Enumerable
      .Range(0, 1 << word.Length)
      .Select(mask => string.Concat(word.Select((c, i) => (mask & (1 << i)) == 0 
         ? char.ToLower(c) 
         : char.ToUpper(c))))
      .ToList();

Demo:

  Console.Write(string.Join(", ", permutate("happy")));

Outcome:

happy, Happy, hAppy, HAppy, haPpy, HaPpy, hAPpy, HAPpy, hapPy, HapPy, hApPy, HApPy, haPPy, HaPPy, hAPPy, HAPPy, happY, HappY, hAppY, HAppY, haPpY, HaPpY, hAPpY, HAPpY, hapPY, HapPY, hApPY, HApPY, haPPY, HaPPY, hAPPY, HAPPY 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!