String Reduction is a programming contest. Necessary Solution

I have a question that asks us to reduce the string as follows.

Input is a string containing only A , B or C The output should be the length of the given string.

The string can be reduced using the following rules:

If any two different letters are adjacent, these two letters can be replaced by the third letter.

Eg ABA β†’ CA β†’ B So the final answer is 1 (the length of the reduced string)

Eg ABCCCCCCC

It does not become the CCCCCCCC , as it can be reduced alternatively by

ABCCCCCCC β†’ AACCCCCC β†’ ABCCCCC β†’ AACCCC β†’ ABCCC β†’ AACC β†’ ABC β†’ AA

since here the length is 2 <(length CCCCCCCC )

How do you deal with this problem?

Thank you so much!

To make everything clear: the question says that he wants the minimum length of a given string. Thus, in the second example above, two solutions are possible: one is CCCCCCCC and the other is AA . So, 2 is the answer, since the length of AA is 2, which is less than the length of CCCCCCCC = 8.

+7
recursion dynamic-programming puzzle
source share
17 answers

I assume that you are looking for the length of the shortest possible string that can be obtained after recovery.

A simple solution would be to explore all the possibilities in a greedy way and hope that it will not explode exponentially. I am going to write Pseodocode Python here because it is easier to understand (at least for me;)):

 from collections import deque def try_reduce(string): queue = deque([string]) min_length = len(string) while queue: string = queue.popleft() if len(string) < min_length: min_length = len(string) for i in xrange(len(string)-1): substring = string[i:(i+2)] if substring == "AB" or substring == "BA": queue.append(string[:i] + "C" + string[(i+2):]) elif substring == "BC" or substring == "CB": queue.append(string[:i] + "A" + string[(i+2):]) elif substring == "AC" or substring == "CA": queue.append(string[:i] + "B" + string[(i+2):]) return min_length 

I think the main idea is clear: you take a queue ( std::deque should be just fine), add your own string to it, and then implement a simple search in width in space of all possible abbreviations. During the search, you take the first element from the queue, take all possible substrings, perform all possible abbreviations, and click the restored lines back into the queue. All space is examined when the queue becomes empty.

+4
source share

As this question is formulated, there are only three different possibilities:

  • If the string has only one unique character, the length will be the same as the length of the string.

2/3. If a string contains more than one unique character, the length is either 1 or 2, always (based on the character layout).

Edit: As a way of proving the concept, there is some grammar and its extensions here: I should note that although this seems to me reasonable evidence that the length will decrease to 1 or 2, I'm pretty sure that determining which of these lengths will lead to the result is not as trivial as I originally thought (you still have to go through all the options to find out)

 S : A|B|C|() S : S^ 

where () denotes an empty string, and s ^ means any combination of the previous characters [A, B, C, ()].

Extended grammar:

 S_1 : AS^|others S_2 : AAS^|ABS^|ACS^|others S_3 : AAAS^| AABS^ => ACS^ => BS^| AACS^ => ABS^ => CS^| ABAS^ => ACS^ => BS^| ABBS^ => CBS^ => AS^| ABCS^ => CCS^ | AAS^| ACAS^ => ABS^ => CS^| ACBS^ => AAS^ | BBS^| ACCS^ => BCS^ => AS^| 

The same thing will happen with extended grammars, starting with B and C (others). There are interesting cases when we have ACB and ABC (three separate characters in a sequence), these cases lead to grammars, which seem to lead to an increase in length:

 CCS^: CCAS^|CCBS^|CCCS^| CBS^ => AS^| CAS^ => BS^| CCCS^| AAS^: AAAS^|AABS^|AACS^| ACS^ => BS^| ABS^ => CS^| AAAS^| BBS^: BBAS^|BBBS^|BBCS^| BCS^ => AS^| BAS^ => CS^| BBBS^| 

Recursively, they lead to an increase in length when the remaining line contains only their value. However, we must remember that this case can also be simplified, because if we got to this area using CCCS ^, then at some point we previously had ABC (or, therefore, CBA). If we look back, we could make better decisions:

 ABCCS^ => AACS^ => ABS^ => CS^ CBACS^ => CBBS^ => ABS^ => CS^ 

So, at best, at the end of the line, when we make all the right decisions, we end with the remaining line of 1 character, followed by another 1 character (since we are at the end). At this time, if the symbol is the same, then we have a length of 2, if it is different, then we can reduce one last time, and as a result we get a length of 1.

+7
source share

You can summarize the result based on the individual number of characters of the string. Algo is as follows:

go through the line and get an individual char account.

Suppose if

  • a = no # from the given string
  • b = no # of b in the given row
  • c = no # c in the given string

then you can say that the result will be

 if((a == 0 && b == 0 && c == 0) || (a == 0 && b == 0 && c != 0) || (a == 0 && b != 0 && c == 0) || (a != 0 && b == 0 && c == 0)) { result = a+b+c; } else if(a != 0 && b != 0 && c != 0) { if((a%2 == 0 && b%2 == 0 && c%2 == 0) || (a%2 == 1 && b%2 == 1 && c%2 == 1)) result = 2; else result = 1; } else if((a == 0 && b != 0 && c != 0) || (a != 0 && b == 0 && c != 0) || (a != 0 && b != 0 && c == 0)) { if(a%2 == 0 && b%2 == 0 && c%2 == 0) result = 2; else result = 1; } 
+6
source share

Let us define an automaton with the following rules (K> = 0):

  Incoming: ABC Current: -------------------------- <empty> ABC A(2K+1) A(2K+2) AB AC A(2K+2) A(2K+3) AAB AAC AB CA CB ABC AAB BA ACB BC ABC CCA AAB AAC 

and all the rules obtained by ABC permutations to get a complete definition.

All input strings using a single letter are irreducible. If the input string contains at least two different letters, the final states, such as AB or AAB, can be reduced to one letter, and the final states, such as ABC, can be reduced to two letters.

In the case of ABC, we still need to prove that the input string cannot be reduced to one letter by another recovery sequence.

+3
source share

Compare two characters at a time and replace if both adjacent characters do not match. To get the best solution, run once from the beginning of the line and once from the end of the line. Return the minimum value.

 int same(char* s){ int i=0; for(i=0;i<strlen(s)-1;i++){ if(*(s+i) == *(s+i+1)) continue; else return 0; } return 1; } int reduceb(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=len-1; while(i>0){ if ((*(s+i)) == (*(s+i-1))){ i--; continue; } else { a_sum = (*(s+i)) + (*(s+i-1)); *(s+i-1) = SUM - a_sum; *(s+i) = '\0'; len--; } i--; } if(same(s) == 1){ return strlen(s); } } } int reducef(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=0; while(i<len-1){ if ((*(s+i)) == (*(s+i+1))){ i++; continue; } else { a_sum = (*(s+i)) + (*(s+i+1)); *(s+i) = SUM - a_sum; int j=i+1; for(j=i+1;j<len;j++) *(s+j) = *(s+j+1); len--; } i++; } if(same(s) == 1){ return strlen(s); } } } int main(){ int n,i=0,f=0,b=0; scanf("%d",&n); int a[n]; while(i<n){ char* str = (char*)malloc(101); scanf("%s",str); char* strd = strdup(str); f = reducef(str); b = reduceb(strd); if( f > b) a[i] = b; else a[i] = f; free(str); free(strd); i++; } for(i=0;i<n;i++) printf("%d\n",a[i]); 

}

+2
source share
 import java.io.*; import java.util.*; class StringSim{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); StringTokenizer st = new StringTokenizer(sc.nextLine(), " "); int N = Integer.parseInt(st.nextToken()); String op = ""; for(int i=0;i<N;i++){ String str = sc.nextLine(); op = op + Count(str) + "\n"; } System.out.println(op); } public static int Count( String str){ int min = Integer.MAX_VALUE; char pre = str.charAt(0); boolean allSame = true; //System.out.println("str :" + str); if(str.length() == 1){ return 1; } int count = 1; for(int i=1;i<str.length();i++){ //System.out.println("pre: -"+ pre +"- char at "+i+" is : -"+ str.charAt(i)+"-"); if(pre != str.charAt(i)){ allSame = false; char rep = (char)(('a'+'b'+'c')-(pre+str.charAt(i))); //System.out.println("rep :" + rep); if(str.length() == 2) count = 1; else if(i==1) count = Count(rep+str.substring(2,str.length())); else if(i == str.length()-1) count = Count(str.substring(0,str.length()-2)+rep); else count = Count(str.substring(0,i-1)+rep+str.substring(i+1,str.length())); if(min>count) min=count; }else if(allSame){ count++; //System.out.println("count: " + count); } pre = str.charAt(i); } //System.out.println("min: " + min); if(allSame) return count; return min; } } 
+2
source share

Wouldn't it be a good start to consider which letter you have the most and look for ways to delete it? Keep doing this until we have only one letter. We can have many times, but for now it’s all the same, we don’t care, we are done.

To avoid getting what ABCCCCCCC becomes CCCCCCCC.

We delete the most popular email:

-ABCCCCCCC
-AACCCCCC
-ABCCCCC
-AACCCC
-ABCCC
-AACC
-abc
-AA

I disagree with the previous poster, which states that we must be 1 or 2 in length - what happens if I enter the AAA start line?

+1
source share
  import java.util.LinkedList; import java.util.List; import java.util.Scanner; public class Sample { private static char[] res = {'a', 'b', 'c'}; private char replacementChar(char a, char b) { for(char c : res) { if(c != a && c != b) { return c; } } throw new IllegalStateException("cannot happen. you must've mucked up the resource"); } public int processWord(String wordString) { if(wordString.length() < 2) { return wordString.length(); } String wordStringES = reduceFromEnd(reduceFromStart(wordString)); if(wordStringES.length() == 1) { return 1; } String wordStringSE = reduceFromStart(reduceFromEnd(wordString)); if(wordString.length() == 1) { return 1; } int aLen; if(isReduced(wordStringSE)) { aLen = wordStringSE.length(); } else { aLen = processWord(wordStringSE); } int bLen; if(isReduced(wordStringES)) { bLen = wordStringES.length(); } else { bLen = processWord(wordStringES); } return Math.min(aLen, bLen); } private boolean isReduced(String wordString) { int length = wordString.length(); if(length < 2) { return true; } for(int i = 1; i < length; ++i) { if(wordString.charAt(i) != wordString.charAt(i - 1)) { return false; } } return wordString.charAt(0) == wordString.charAt(length - 1); } private String reduceFromStart(String theWord) { if(theWord.length() < 2) { return theWord; } StringBuilder buffer = new StringBuilder(); char[] word = theWord.toCharArray(); char curChar = word[0]; for(int i = 1; i < word.length; ++i) { if(word[i] != curChar) { curChar = replacementChar(curChar, word[i]); if(i + 1 == word.length) { buffer.append(curChar); break; } } else { buffer.append(curChar); if(i + 1 == word.length) { buffer.append(curChar); } } } return buffer.toString(); } private String reduceFromEnd(String theString) { if(theString.length() < 2) { return theString; } StringBuilder buffer = new StringBuilder(theString); int length = buffer.length(); while(length > 1) { char a = buffer.charAt(0); char b = buffer.charAt(length - 1); if(a != b) { buffer.deleteCharAt(length - 1); buffer.deleteCharAt(0); buffer.append(replacementChar(a, b)); length -= 1; } else { break; } } return buffer.toString(); } public void go() { Scanner scanner = new Scanner(System.in); int numEntries = Integer.parseInt(scanner.nextLine()); List<Integer> counts = new LinkedList<Integer>(); for(int i = 0; i < numEntries; ++i) { counts.add((processWord(scanner.nextLine()))); } for(Integer count : counts) { System.out.println(count); } } public static void main(String[] args) { Sample solution = new Sample(); solution.go(); } } 
+1
source share

This is a greedy approach and crossing the path begins with every possible pair and checking the minimum length.

 import java.io.*; import java.util.*; class StringSim{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); StringTokenizer st = new StringTokenizer(sc.nextLine(), " "); int N = Integer.parseInt(st.nextToken()); String op = ""; for(int i=0;i<N;i++){ String str = sc.nextLine(); op = op + Count(str) + "\n"; } System.out.println(op); } public static int Count( String str){ int min = Integer.MAX_VALUE; char pre = str.charAt(0); boolean allSame = true; //System.out.println("str :" + str); if(str.length() == 1){ return 1; } int count = 1; for(int i=1;i<str.length();i++){ //System.out.println("pre: -"+ pre +"- char at "+i+" is : -"+ str.charAt(i)+"-"); if(pre != str.charAt(i)){ allSame = false; char rep = (char)(('a'+'b'+'c')-(pre+str.charAt(i))); //System.out.println("rep :" + rep); if(str.length() == 2) count = 1; else if(i==1) count = Count(rep+str.substring(2,str.length())); else if(i == str.length()-1) count = Count(str.substring(0,str.length()-2)+rep); else count = Count(str.substring(0,i-1)+rep+str.substring(i+1,str.length())); if(min>count) min=count; }else if(allSame){ count++; //System.out.println("count: " + count); } pre = str.charAt(i); } //System.out.println("min: " + min); if(allSame) return count; return min; } } 
+1
source share

Following the observations of NominSim, perhaps this is the optimal solution that is executed in linear time using the O (1) space. Note that it is only able to find the length of the smallest reduction, and not the reduced string:

 def reduce(string): a = string.count('a') b = string.count('b') c = string.count('c') if ([a,b,c].count(0) >= 2): return a+b+c elif (all(v % 2 == 0 for v in [a,b,c]) or all(v % 2 == 1 for v in [a,b,c])): return 2 else: return 1 
+1
source share

There is some basic structure that can be used to solve this problem in O (n) time.

The above rules are (most) rules defining a mathematical group, in particular the group D_2, sometimes also known as K (for four Klein groups) or V (German for Viergruppe, four groups). D_2 is a group with four elements, A, B, C and 1 (single element). One of the implementations of D_2 is the set of symmetries of a rectangular box with three different sides. A, B and C - rotation of 180 degrees along each of the axes, and 1 - rotation of the identity (without rotation). The group table for D_2 is

  |1 ABC -+------- 1|1 ABC A|A 1 CB B|BC 1 A C|CBA 1 

As you can see, the rules correspond to the rules specified in the task, except that the rules associated with 1 are not present in the task.

Since D_2 is a group, it satisfies a number of rules: closure (the product of any two elements of the group is another element), associativity (which means (x*y)*z = x*(y*z) for any elements x , y , z , i.e., the order in which the lines are shortened does not matter), the existence of identity (there is an element 1 such that 1*x=x*1=x for any x ) and the existence of the converse (for any element x such an element x^{-1} such that x*x^{-1}=1 and x^{-1}*x=1 , in our case each element is its own inverse).

It is also worth noting that D_2 is commutative , i.e. x*y=y*x for any x,y .

For any row of elements in D_2, we can greedily reduce to one element in a group . For example, ABCCCCCCC=CCCCCCCC=CCCCCC=CCCC=CC=1 . Please note that we do not write element 1 , unless it is the only element in the row. Associativity tells us that the order of operations does not matter, for example, we could work from right to left or start in the middle and get the same result. Try right: ABCCCCCCC=ABCCCCC=ABCCC=ABC=AA=1 .

The situation is different from the fact that operations from 1 not allowed, so we cannot just exclude pairs AA , BB or CC . However, the situation is not so different. Consider the string ABB . We cannot write ABB=A in this case. However, we can eliminate BB in two steps using A : ABB=CB=A Since the order of work does not matter by associativity, we are guaranteed to get the same result. Therefore, we cannot go from ABB to A , but we can get the same result on a different route.

Such alternative routes are available if there are at least two different elements in the line. In particular, in each of ABB , ACC , BAA , BCC , CAA , CBB , AAB , AAC , BBA , BBC , CCA , CCB , we can act as if we have a reduction xx=1 , and then discard 1 .

It follows that any string that is not uniform (not every letter) and has a two-letter substring ( AA , BB , or CC ) can be reduced by removing the double letter. Lines containing only two identical letters cannot be further reduced (because 1 not resolved in the problem), therefore it seems safe to assume that any heterogeneous line can be reduced to A , B , C , AA , BB , CC .

We still have to be careful, because CCAACC could be turned into a converter by removing the middle pair AA , but this is not the best we can do: CCAACC=AACC=CC or AA takes us to a string of length 2.

Another situation that we must be careful about is AABBBB . Here we could exclude AA to finish with BBBB , but first it’s better to remove middle B , then anything: AABBBB=AABB=AA or BB (both of which are equivalent to 1 in the group, but cannot be further reduced in the problem).

There was another interesting situation: AAAABBBB . Blind pair removal leads us to AAAA or BBBB , but we could do better: AAAABBBB=AAACBBB=AABBBB=AABB=AA or BB .

The foregoing indicates that the exclusion of doubles blindly is not necessarily a continuation, but nevertheless it was covered.

Instead, it seems that the most important property of the string is heterogeneity. If the line is uniform, stop, we can do nothing. Otherwise, define an operation that preserves the heterogeneity property if possible. I argue that it is always possible to identify an operation that preserves heterogeneity if the string is heterogeneous and has a length of four or more.

Proof: if a 4-substring contains two different letters, a third letter can be entered at the border between two different letters, for example, AABA goes into ACA . Since one or the other original letter must be invariable somewhere inside the string, it follows that the result is still heterogeneous.

Suppose instead that we have a 4-substring that has three different elements, such as AABC , with the outer two elements different. Then, if the middle two elements are different from each other, perform an operation on them; the result is heterogeneous because the two outer elements are still different. On the other hand, if two internal elements are the same, for example, ABBC , then they must be different from both external elements (otherwise we will only have two elements in a set of four, not three). In this case, perform the first or third operation; which leaves either the last two elements different (for example, ABBC=CBC ), or the first two different elements (for example, ABBC=ABA ), so unevenness persists.

Finally, consider the case where the first and last elements are the same. Then we have a situation like ABCA . The middle two elements both should be different from the outer elements, otherwise in this case we will have only two elements, not three. We can take the first available operation ABCA=CCA , and the heterogeneity persists again.

The end of the proof.

We have a greedy algorithm for cutting any heterogeneous string 4 or longer in length: select the first operation that preserves the heterogeneity; such an operation must exist by the argument above.

Now we came down to the case when we have a heterogeneous string of three elements. If the two are the same, we either have doublings like AAB , etc. Which, as we know, can be reduced to one element or we have two elements without a double type ABA=AC=B , which can also be reduced to one element, or we have three different elements, such as ABC . There are six permutations, all of which =1 in the group of associativity and commutativity; all of them can be reduced to two elements by any operation; however, they cannot be reduced below a homogeneous pair ( AA , BB or CC ), since 1 not allowed in the problem, so we know that the best we can do in this case.

So, if the string is uniform, we can do nothing; if the string is heterogeneous and =A in the group, it can be reduced to A in the problem by a greedy algorithm that preserves heterogeneity at every step; the same if the string =B or =C in the group; finally, if the string is heterogeneous and =1 in the group, it can be reduced using the greedy algorithm, which keeps the unevenness as long as possible to one of AA , BB or CC . This is the best we can do with the group properties of the operation.

The program that solves the problem:

Now, since we know the possible results, our program can work in O(n) time as follows: if all the letters in a given string are the same, then reduction is impossible, so just print the length of the string. If the string is heterogeneous and equal to one in the group, print number 2; otherwise print number 1.

, , : A 's, B C A , B , C . a = a mod 2 , b = b mod 2 , c = c mod 2 , AA , BB CC . A , B , C 0, ABC=1 , 2, 1 . A , B , C 0, ( A , B C ), 2 , 1.

+1
source share
 //C# Coding using System; using System.Collections.Generic; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { /* Keep all the rules in Dictionary object 'rules'; key - find string, value - replace with value eg: find "AB" , replace with "AA" */ Dictionary<string, string> rules = new Dictionary<string, string>(); rules.Add("AB", "AA"); rules.Add("BA", "AA"); rules.Add("CB", "CC"); rules.Add("BC", "CC"); rules.Add("AA", "A"); rules.Add("CC", "C"); // example string string str = "AABBCCCA"; //output Console.WriteLine(fnRecurence(rules, str)); Console.Read(); } //funcation for applying all the rules to the input string value recursivily static string fnRecurence(Dictionary<string, string> rules,string str) { foreach (var rule in rules) { if (str.LastIndexOf(rule.Key) >= 0) { str = str.Replace(rule.Key, rule.Value); } } if(str.Length >1) { int find = 0; foreach (var rule in rules) { if (str.LastIndexOf(rule.Key) >= 0) { find = 1; } } if(find == 1) { str = fnRecurence(rules, str); } else { //if not find any exit find = 0; str = str; return str; } } return str; } } 

}

+1
source share

, . , . .

Rav: -

 int same(char* s){ int i=0; for(i=0;i<strlen(s)-1;i++){ if(*(s+i) == *(s+i+1)) continue; else return 0; } return 1; } int reduceb(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=len-1; while(i>0){ if ((*(s+i)) == (*(s+i-1))){ i--; continue; } else { a_sum = (*(s+i)) + (*(s+i-1)); *(s+i-1) = SUM - a_sum; *(s+i) = '\0'; len--; } i--; } if(same(s) == 1){ return strlen(s); } } } int reducef(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=0; while(i<len-1){ if ((*(s+i)) == (*(s+i+1))){ i++; continue; } else { a_sum = (*(s+i)) + (*(s+i+1)); *(s+i) = SUM - a_sum; int j=i+1; for(j=i+1;j<len;j++) *(s+j) = *(s+j+1); len--; } i++; } if(same(s) == 1){ return strlen(s); } } } int main(){ int n,i=0,f=0,b=0; scanf("%d",&n); int a[n]; while(i<n){ char* str = (char*)malloc(101); scanf("%s",str); char* strd = strdup(str); f = reducef(str); b = reduceb(strd); if( f > b) a[i] = b; else a[i] = f; free(str); free(strd); i++; } for(i=0;i<n;i++) printf("%d\n",a[i]); 

}

@Rav

"abccaccba". "b" . (- ), .

0
source share

. , . - .

0
source share

2- .

 len = strlen (str) ; index = 0 ; flag = 0 ; /* 1st pass */ for ( i = len-1 ; i > 0 ; i -- ) { if ( str[i] != str[i-1] ) { str[i-1] = getChar (str[i], str[i-1]) ; if (i == 1) { output1[index++] = str[i-1] ; flag = 1 ; break ; } } else output1[index++] = str[i] ; } if ( flag == 0 ) output1[index++] = str[i] ; output1[index] = '\0'; 

'output1', . , One - , - .

0
source share
  int previous = a.charAt(0); boolean same = true; int c = 0; for(int i = 0; i < a.length();++i){ c ^= a.charAt(i)-'a'+1; if(a.charAt(i) != previous) same = false; } if(same) return a.length(); if(c==0) return 2; else return 1; 
0
source share
 import java.util.Scanner; public class StringReduction { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String str = sc.nextLine(); int length = str.length(); String result = stringReduction(str); System.out.println(result); } private static String stringReduction(String str) { String result = str.substring(0); if(str.length()<2){ return str; } if(str.length() == 2){ return combine(str.charAt(0),str.charAt(1)); } for(int i =1;i<str.length();i++){ if(str.charAt(i-1) != str.charAt(i)){ String temp = str.substring(0, i-1) + combine(str.charAt(i-1),str.charAt(i)) + str.substring(i+1, str.length()); String sub = stringReduction(temp); if(sub.length() < result.length()){ result = sub; } } } return result; } private static String combine(char c1, char c2) { if(c1 == c2){ return "" + c1 + c2; } else{ if(c1 == 'a'){ if(c2 == 'b'){ return "" + 'c'; } if(c2 == 'c') { return "" + 'b'; } } if(c1 == 'b'){ if(c2 == 'a'){ return "" + 'c'; } if(c2 == 'c') { return "" + 'a'; } } if(c1 == 'c'){ if(c2 == 'a'){ return "" + 'b'; } if(c2 == 'b') { return "" + 'a'; } } return null; } } 

}

0
source share

All Articles