Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Interviewer asked a question in an interview to write fast & efficient algorithm for below function,

Problem: Write a function to parse a given string for below given rules & produce final parsed string as output

write a function which will accepts string as input, string length will be in between [0..2000000000]

string should be made from only 'A','B' & 'C' characters like 'AAA' ,'ABCABC','AAAABBBBABAAACCCA'

Transformation Rules:


1) 'AB' -> 'AA'
2) 'AC' -> 'AA'
3) 'AA' -> 'A'
4) 'CC' -> 'C'
5) 'BC' -> 'BB'
6) 'BB' -> 'B'


Apply all above 6 rules randomly on given string each at a time and make final transformed string as output

For example input to Function is: 'ABCAAB' string

ABCAAB -> AACAAB [AB = AA]
AACAAB -> ACAAB [AA = A]
ACAAB -> AAAAB [AC = AA]
AAAAB -> AAAB [AA = A]
AAAB -> AAB [AA = A]
AAB -> AB [AA = A]
AB -> AA [AB = AA]
AA -> A [AA = A]

Final result: 'A'
Because we can not apply any more rules to the string now.

My Answer was like (pseudocode):

sub myFunc {
my $str = @_;
flag = 1
while(flag ==1) {
    if ($str =~ /AB/){
    $str =~ s/AB/AA/;
    next;
    }
    elsif($str =~ /AC/){
    $str =~ s/AC/AA/;
    next;
    }
    elsif($str =~ /AA/){
    $str =~ s/AA/A/;
    next;
    }
    elsif($str =~ /CC/){ 
    $str =~ s/CC/C/;
    next;
    }
    elsif($str =~ /BC/){ 
    $str =~ s/BC/BB/;
    next;
    }
    elsif($str =~ /BB/){ 
    $str =~ s/BB/B/;
    next;
    }
    else
    {
        flag = 0
    }
} //while
 return $str;
} //func

Can someone suggest better algorithm & approach for above problem ?

share|improve this question
3  
... and what's your question/problem? –  Karoly Horvath 14 hours ago
    
@Karoly: please suggest optimized solution/algorithem for above problem. –  n33rma 14 hours ago
    
How does the above code "apply all 7 rules randomly"? Also (if it doesn't have to be random) wouldn't it be more efficient to use if's instead if else's (as you wouldn't need the while loop)? –  Rob 14 hours ago
    
@Rob: I wrote loop because they told me to apply one rule at a time, so we can't use global identifier 'g' in pattern matching. :( –  n33rma 14 hours ago
1  
There is a trick I guess: the order of the substitutions does not matter. –  Yves Daoust 14 hours ago

4 Answers 4

Rules 1 to 3 will discard any character following an A. Rules 5 and 6 will discard any B and C following a B. Rule 4 will discard any C following a C. The order of the substitutions does not matter.

So after processing the string will be one of C, CB, CA, CBA, B, BA, A.

A single scan of the string will suffice. If you see a C, keep it and skip the next C's; then if you see a B, keep it and skip the next B's; then if you see an A keep it and stop.

The given example ABCAAB immediately yields A.

Solutions with explicit application of the rules and multiple passes are unacceptable as their behavior can be O(N²) or even O(N³), while N=2000000000.

share|improve this answer
    
Thank you for making it so simple –  n33rma 13 hours ago
1  
You are welcome. Seeing the length up to 2000000000 hinted me that there was a trick. –  Yves Daoust 11 hours ago
    
Do you have a good idea how to actually implement it? In my answer I use find(), as it's very fast. But I need two scans. And I can't think of a faster way that scans just once. –  Stefan Pochmann 9 hours ago
    
Three if tests and three while loops. Except for pathological cases, speed is not an issue here, parsing stops at the first A. –  Yves Daoust 7 hours ago
    
@lucaC: thanks for the correction. –  Yves Daoust 7 hours ago

You can repeat substitution while it matches transformation rules,

my %h = (
  'AB' => 'AA',
  'AC' => 'AA', 
  'AA' => 'A', 
  'CC' => 'C', 
  'BC' => 'BB', 
  'BB' => 'B', 
);
my $s = 'ABCAAB';

1 while $s =~ s/(AB|AC|AA|CC|BC|BB)/$h{$1}/; # also without /g switch
print $s;

output

A
share|improve this answer
    
there was a condition to apply only one rule at a time .... so we can't use 'g' –  n33rma 14 hours ago
    
@n33rma there is no /g switch. –  Сухой27 14 hours ago
    
Let me try , Thank you so much for your solution –  n33rma 13 hours ago
1  
Sorry I can't vote your answer , it says "you need 15 reputation to vote" –  n33rma 13 hours ago
    
@n33rma: Now you can vote. You have enough reputation to upvote :) –  serenesat 11 hours ago

here is a python solution:

In [34]: import ranodm
In [35]: rules = {"AB":"AA",'AC':'AA','AA':'A','CC':'C','BC':'BB','BB':'B'}

In [36]: keys = rules.keys()

In [37]: keys
Out[37]: ['AA', 'AC', 'AB', 'BB', 'BC', 'CC']

In [38]: mystr = 'ABCAAB' 

In [42]: while len(mystr)>=2:
    r = random.choice(keys) #choose one rule randomly 
    mystr = mystr.replace(r,rules[r])
   ....:   

In [43]: mystr
Out[43]: 'A'
share|improve this answer
    
Thank you so much for your solution –  n33rma 13 hours ago
    
Yes I tried but it says "you need 15 reputation to vote" Sorry for that. –  n33rma 13 hours ago

Yves Daoust's answer is right, makes no sense to simulate it. Seems like a trick question and that you were supposed to realize that and understand the behavior and implement it directly.

Yves' method works, but here's an actual implementation of a similar one:

def transform(string):
    a = string.find('A') + 1
    b = string.find('B', 0, a or len(string)) + 1
    return 'C' * string.startswith('C') + 'B' * (b>0) + 'A' * (a>0)

I search for the first 'A', then search for a 'B' left of it (or in the whole string, if there was no 'A'). That tells me whether 'B' and 'A' belong into the output. For 'C' I just need to check the start of the string. While I potentially scan the whole string twice rather than just once like Yves suggests, using the find function makes pretty it fast, about a 100 times faster than just looping over the string "by hand" and just looking for an 'A' (which is only at the end of my teststring):

>>> from timeit import timeit
>>> s = 'C' * 100000000 + 'BA'

>>> timeit(lambda: transform(s), number=1)
0.10583857561359977

>>> timeit(lambda: next(x for x in s if x == 'A'), number=1)
10.957446603791382

You could do it with just one scan by using lstrip('C') to find the first non-'C' character, and that's faster than doing it by hand, but that uses extra memory and is still much slower than find:

>>> timeit(lambda: s.lstrip('C'), number=1)
2.411250071361735

Regular expressions could probably also do it, but even just scanning my teststring once just looking for 'A' takes longer than my whole transform:

>>> timeit(lambda: re.search('A', s), number=1)
0.13403880409939006
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.