Chor Wai Chun
Chor Wai Chun

Reputation: 3236

C# Finding "connected" permutations

I'm working on a dictionary table and required to find out all possible combination of characters in a word. Thanks to https://codereview.stackexchange.com/questions/28248/implement-a-function-that-prints-all-possible-combinations-of-the-characters-in , I got below working so far:

    public List<string> findAllOccurance(string str)
    {
        var results = from e in Range(0, BigInteger.Pow(2, str.Length))
             let p =
                 from b in Enumerable.Range(1, str.Length)
                 select (e & BigInteger.Pow(2, b - 1)) == 0 ? (char?)null : str[b - 1]
             select string.Join(string.Empty, p);

        return results.ToList();
    }

    public IEnumerable<BigInteger> Range(BigInteger start, BigInteger count)
    {
        while (count-- > 0)
        {
            yield return start++;
        }
    }

Passing "abc" to above function would returns:

a
b
ab
c
ac
bc
abc

The problem is I actually would like to find out only the "connected" permutations in "original order", for example "abc" should return only

a
b
c
ab
bc
abc

Does anyone have any idea what should I change to achieve above?

Upvotes: 0

Views: 77

Answers (3)

PaulF
PaulF

Reputation: 6773

By "connected" permutations - you are effectively looking for all substrings from length 1 up to the full length of the string. This can be very easily done with two for loops. The duplicates can be removed by using Linq's Distinct() method.

public List<string> findAllOccurance(string str)
{
    List<string> result = new List<string>();
    for (int i = 1; i <= str.Length; i++)
    {
      for (int j=0; j <= str.Length-i; j++)
        result.Add(str.Substring(j,i));
    }
    return result.Distinct().ToList();
}

NOTE - if you really do want to return an empty string - you can either modify the outer loop to start from 0 or simply manually add it after creating the list instance. Modifying the loop will result in str.Length empty strings being added & more work for Distinct() when you know you will only ever always want 1 empty string returned.

List<string> result = new List<string>();
result.Add(String.Empty);
for (int i = 1; i <= str.Length; i++)
.....

Upvotes: 2

Dblaze47
Dblaze47

Reputation: 868

For your code, you are performing bitwise operation in order to find all possible subsets. For the case abc your string length is 3. So possible subsets = 2 ^ 3 = 8. Writing them down and matching the rightmost bit with the leftmost index:

Possible cases: 
0 0 0     // gives nothing
0 0 1     // gives 'a' (valid)
0 1 0     // gives 'b' (valid)
0 1 1     // gives 'ab' (valid)
1 0 0     // gives 'c' (valid)
1 0 1     // gives 'ac' (invalid as there is a 0 inbetween and not connected)
1 1 0     // gives 'bc' (valid)
1 1 1     // gives 'abc' (valid)

The above is what I understand for what you count as connected. You can easily perform a check to do this with two loops:

int max_size = BigInteger.Pow(2, str.Length);
int str_size = str.Length;
for(int i = 0; i < max_size; ++i) 
{ 
    string ans = "";
    for(j = 0; j < str_size; ++j) 
   { 
      // We check if the jth bit is set, we print the jth element from the string.
       if(i & (1 << j)) 
           ans += str[j];
       } 
       else {
           if(ans.Length > 0){
               // this means we have already added a character and then the next consecutive bit is '0'
               ans = "";
               break;
           }
       }
      if(ans != ""){
           Console.Writeline(ans); // we print the set. you can control this anyway you want.
      } 
   }   
} 

Upvotes: 1

Bart Hofland
Bart Hofland

Reputation: 3905

I don't know if I understand "connected" right... Maybe you could simply check if a potential result is a part of the original string... Something like this:

public List<string> findAllOccurance(string str)
{
    var results = from e in Range(0, BigInteger.Pow(2, str.Length))
         let p =
             from b in Enumerable.Range(1, str.Length)
             select (e & BigInteger.Pow(2, b - 1)) == 0 ? (char?)null : str[b - 1]
         let p2 = string.Join(string.Empty, p)
         where str.Contains(p2)
         select p2;

    return results.ToList();
}

public IEnumerable<BigInteger> Range(BigInteger start, BigInteger count)
{
    while (count-- > 0)
    {
        yield return start++;
    }
}

Upvotes: 1

Related Questions