【问题标题】:String Reduction - Programming Contest . Solution needed字符串缩减 - 编程比赛。需要解决方案
【发布时间】:2012-01-23 00:05:55
【问题描述】:

我有一个问题要求我们按如下方式减少字符串。

输入是一个只有ABC 的字符串。输出长度必须为 缩小的字符串

字符串可以通过以下规则进行缩减

如果任意2个不同的字母相邻,这两个字母可以 替换为第三个字母。

例如 ABA -> CA -> B 。所以最终答案是 1(减少字符串的长度)

例如ABCCCCCCC

这不会变成CCCCCCCC,因为它可以减少

ABCCCCCCC->AACCCCCC->ABCCCCC->AACCCC->ABCCC->AACC->ABC->AA

因为这里的长度是 2 CCCCCCCC)

你如何解决这个问题?

非常感谢!

为了清楚起见:问题表明它想要减少字符串的最小长度。所以在上面的第二个例子中,有两种可能的解决方案,一种是CCCCCCCC,另一种是AA。所以 2 是答案,因为 AA 的长度是 2,它小于 CCCCCCCC = 8 的长度。

【问题讨论】:

  • ABCCCCCCC 如何变成AACCCCCC?该规则建议AB 变为C
  • @Oli Charlesworth:我猜你在这一步将BC 减少到A 以获得AACCCCCC。换句话说,不需要减少最左边的匹配子字符串,您可以减少由两个不同字符组成的任何子字符串。
  • @user1055058 你是如何解决这个问题的?你试过了吗?
  • 我认为您可以执行动态编程之类的操作并递归调用该函数并尽一切可能......
  • @Oli Charlesworth:是的,肯定有多种解决方案。我在这里同意霍华德的观点,即发帖人对尽可能短的简化字符串感兴趣。是的,这个问题还不清楚——我想我只是根据自己对此类难题的经验填补了空白;)

标签: recursion dynamic-programming puzzle


【解决方案1】:

这个问题的表述方式,只有三种不同的可能性:

  1. 如果字符串只有一个唯一字符,则长度与字符串长度相同。

2/3。如果字符串包含多个唯一字符,则长度始终为 1 或 2(基于字符的布局)。

编辑: 作为概念证明的一种方式,这里有一些语法及其扩展: 我应该注意到,尽管在我看来这似乎是一个合理的证据,证明长度将减少到 1 或 2,但我有理由确定确定这些长度中的哪一个不会像我最初想象的那样微不足道(你仍然会必须递归所有选项才能找到它)

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

其中 () 表示空字符串,s^ 表示前面的 [A,B,C,()] 字符的任意组合。

扩展语法:

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^|

以 B 和 C(其他)开头的扩展语法也会发生同样的情况。有趣的情况是我们有 ACB 和 ABC(三个不同的字符顺序),这些情况导致语法似乎导致更长的长度:

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^|

当剩余的字符串只包含它们的值时,它们只会导致更长的长度。但是我们必须记住,这种情况也可以简化,因为如果我们用 CCCS^ 到达这个区域,那么我们之前有过 ABC(或因此 CBA)。如果我们回顾过去,我们本可以做出更好的决定:

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

所以在最好的情况下,当我们做出所有正确的决定时,我们以剩余的 1 个字符的字符串结束,然后再添加 1 个字符(因为我们在末尾)。这时候如果字符相同,那么我们的长度为2,如果不同,那么我们可以最后一次减少一个,最终得到长度为1。

【讨论】:

【解决方案2】:

您可以根据字符串的单个字符数来概括结果。算法如下,

遍历字符串并获取单个字符数。

假设

  • a = 给定字符串中没有 # 个 a
  • b = 给定字符串中没有 # 个 b
  • c = 给定字符串中没有 # 个 c

那么你可以这么说,结果会是,

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;
}

【讨论】:

  • 你有你算法的证明吗?字符数和推导规则之间的关系不是很明显。
  • @NominSim 这是正确的算法,你只需要在纸上做几个例子,(odd , even) 的每个 a , b , c 的频率不同,而且位置也可以互换。
【解决方案3】:

我假设您正在寻找减少后可以获得的最短字符串的长度。

一个简单的解决方案是以贪婪的方式探索所有可能性,并希望它不会呈指数级增长。我将在这里编写 Python 伪代码,因为这更容易理解(至少对我而言;)):

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

我认为基本思想很明确:你拿一个队列(std::deque 应该没问题),将你的字符串添加到其中,然后在所有可能的缩减空间中实现简单的广度优先搜索。在搜索过程中,您从队列中取出第一个元素,取出它所有可能的子字符串,执行所有可能的归约,并将归约后的字符串推回队列。当队列变空时探索整个空间。

【讨论】:

  • 这不会一直有效。在第二种情况下,您将 AB 替换为 C,答案将是 CCCCCCCC
  • 是的,我会,但我也会在备用路径中将BC 替换为A,并将两条路径都添加到队列中。
  • 其实我已经在 Python 中试过了,它适用于你给出的两个例子。
  • Ok yours 负责所有可能的解决方案。如果我错了,请纠正我。在数组的一次遍历中,您替换所有出现的不同相邻字符并将其添加到队列中?
  • 在一次通过中,我获取当前正在检查的字符串中所有可能的相邻字符对,如果它们不同,我创建缩减并将其添加到队列中。有空我会贴出 C++ 代码。
【解决方案4】:

让我们定义一个具有以下规则 (K>=0) 的自动机:

   Incoming:    A       B       C
Current:    --------------------------
<empty>         A       B       C
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

以及通过ABC排列得到的所有规则得到完整的定义。

所有使用单个字母的输入字符串都是不可约的。如果输入字符串至少包含两个不同的字母,则 AB 或 AAB 等最终状态可以简化为单个字母,ABC 等最终状态可以简化为两个字母。

在 ABC 的情况下,我们仍然要证明输入字符串不能被另一个归约序列归约为单个字母。

【讨论】:

  • 嗨,埃里克·班维尔。很好的答案-您是否有任何文档/链接可以备份它并且其他人可以从中学习?谢谢!
  • 这是最简单的自动机,保留最后一个字母,不引入双字母。我的回答不完整,因为它不能证明 2 在“ABC 案例”中是最小的。
【解决方案5】:

一次比较两个字符,如果两个相邻字符不相同则替换。要获得最佳解决方案,请从字符串的开头运行一次,从字符串的结尾运行一次。返回最小值。

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]);

}

【讨论】:

    【解决方案6】:
    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;
        }
    
    }
    

    【讨论】:

      【解决方案7】:

      算出你拥有最多的字母并想办法删除它不是一个好的开始吗?继续这样做,直到我们只有一封信。我们可能有很多次,但只要它是相同的,我们不在乎,我们就完成了。

      为了避免让 ABCCCCCCC 变成 CCCCCCCC。

      我们删除最受欢迎的字母:

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

      我不同意之前发帖者的说法,即我们的长度必须为 1 或 2 - 如果我输入起始字符串 AAA 会发生什么?

      【讨论】:

      • 如果有多个唯一字符,则长度必须为 1 或 2。
      • 是的,但是如果只输入一个字符会发生什么?例如 AAAAAAA。这里的字符串不能减少,因此我们不能假设结果字符串总是小于 3。
      • 这就是为什么我说如果有多个唯一字符...仅输入一个字符的字符串是微不足道的情况,因为它们无法减少。显然你想包括这些长度,但这只是一种特殊情况,其他减少的情况在 imo 中更为重要,因为它们实际上可以减少。
      【解决方案8】:
          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();
          }
      }
      

      【讨论】:

      • 通过给定的测试用例:assertEquals(sampleInstance.processWord("abccccccc"), 2);
      【解决方案9】:

      这是一种贪婪的方法,遍历路径从每个可能的对开始并检查最小长度。

      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;
        }
      }
      

      【讨论】:

        【解决方案10】:

        根据 NominSim 的观察,这可能是一个最佳解决方案,它在 O(1) 空间使用量的线性时间内运行。请注意,它只能找到最小缩减的长度,而不是缩减的字符串本身:

        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
        

        【讨论】:

          【解决方案11】:

          有一些底层结构可以用来在 O(n) 时间内解决这个问题。

          给出的规则是(大部分)定义数学群的规则,特别是 D_2 群,有时也称为 K(Klein 的四群)或 V(Viergruppe 的德语四群)。 D_2 是具有四个元素 A、B、C 和 1(标识元素)的组。 D_2 的实现之一是具有三个不同边的矩形框的对称集。 A、B 和 C 是围绕每个轴旋转 180 度,1 是恒等旋转(无旋转)。 D_2 的组表是

           |1 A B C
          -+------- 
          1|1 A B C
          A|A 1 C B
          B|B C 1 A
          C|C B A 1
          

          如您所见,规则与问题中给出的规则相对应,只是涉及1的规则在问题中不存在。

          由于D_2是一个群,它满足一些规则:闭包(群中任意两个元素的乘积是另一个元素),关联性(意思是@ 987654322@ 用于任何元素xyz;即字符串减少的顺序无关紧要),存在身份(有一个元素1这样1*x=x*1=x 对于任何x),并且存在逆向(对于任何元素x,都有一个元素x^{-1} 使得x*x^{-1}=1 and x^{-1}*x=1;在我们的例子中,每个元素是它自己的逆)。

          还值得注意的是,D_2 是可交换的,即x*y=y*x 对应于任何x,y

          给定 D_2 中的任何元素字符串,我们可以以贪婪的方式将组中的单个元素缩减为单个元素。例如,ABCCCCCCC=CCCCCCCC=CCCCCC=CCCC=CC=1。注意我们不写元素1,除非它是字符串中唯一的元素。关联性告诉我们操作的顺序无关紧要,例如,我们可以从右到左工作或从中间开始并得到相同的结果。让我们从右边尝试:ABCCCCCCC=ABCCCCC=ABCCC=ABC=AA=1

          问题的情况不同,因为涉及1的操作是不允许的,所以我们不能只消除对AABBCC。然而,情况并没有那么不同。考虑字符串ABB。在这种情况下我们不能写ABB=A。但是,我们可以使用A 分两步消除BBABB=CB=A。由于操作顺序与关联性无关,因此我们保证得到相同的结果。所以我们不能直接从ABBA 但我们可以通过另一条路线得到相同的结果。

          只要字符串中至少有两个不同的元素,就可以使用这种备用路由。特别地,在ABBACCBAABCCCAACBBAABAACBBACCA57@3中的每一个中CCB,我们可以假设我们有减少xx=1,然后删除1

          因此,可以通过删除双字母来减少任何不齐次(不是所有相同字母)并且具有双字母子字符串(AABBCC)的字符串。仅包含两个相同字母的字符串无法进一步缩减(因为问题中不允许使用1),因此假设任何非同质字符串都可以缩减为AB 似乎是安全的,CAABBCC

          然而,我们仍然必须小心,因为 CCAACC 可以通过删除中间对 AA 变成 CCCC,但这不是我们能做的最好的事情:CCAACC=AACC=CC or AA 将我们带到长度为 2 的字符串。

          我们必须注意的另一种情况是AABBBB。这里我们可以去掉AA,以BBBB结尾,但最好先去掉中间的B,然后再去掉:AABBBB=AABB=AA or BB(两者都相当于组内的1,但可以'不会在问题中进一步减少)。

          还有另一个有趣的情况:AAAABBBB。盲目消除对将我们带到AAAABBBB,但我们可以做得更好:AAAABBBB=AAACBBB=AABBBB=AABB=AA or BB

          以上表明,盲目消除双打不一定是前进的方式,但它仍然是有启发性的。

          相反,字符串最重要的属性似乎是非同质性。如果字符串是同质的,停止,我们无能为力。否则,如果可能,确定一个保留非同质性属性的操作。我断言,如果字符串是非同质的并且长度为 4 或更大,则始终可以识别出保持非同质性的操作。

          证明:如果一个 4 子串包含两个不同的字母,则可以在两个不同字母之间的边界处引入第三个字母,例如,AABA 转到 ACA。由于原始字母中的一个或另一个必须在字符串中的某处保持不变,因此结果仍然是非齐次的。

          假设我们有一个 4 子字符串,它具有三个不同的元素,比如AABC,外部两个元素不同。那么如果中间两个元素不同,就对它们进行操作;结果是不均匀的,因为最外面的两个元素仍然不同。另一方面,如果两个内部元素相同,例如ABBC,那么它们必须与两个最外面的元素不同(否则我们在四个元素的集合中只有两个元素,而不是三个)。在这种情况下,执行第一个或第三个操作;这使得最后两个元素不同(例如,ABBC=CBC)或前两个元素不同(例如,ABBC=ABA),因此保留了非同质性。

          最后,考虑第一个和最后一个元素相同的情况。然后我们遇到ABCA 这样的情况。中间的两个元素都必须与外部元素不同,否则在这种情况下我们只有两个元素,而不是三个。我们可以取第一个可用的操作,ABCA=CCA,再次保留非同质性。

          证明结束。

          我们有一个贪心算法来减少任何长度为 4 或更大的非齐次字符串:选择第一个保持非齐次的操作;这样的操作必须存在上述论点。

          我们现在已经简化到我们有一个由 3 个元素组成的非同质字符串的情况。如果两个相同,我们要么有像AAB 这样的双精度,我们知道可以简化为单个元素,或者我们有两个没有双精度的元素,例如ABA=AC=B,也可以简化为单个元素,或者我们有三个不同的元素,比如ABC。共有六个排列,全部=1按关联性和交换性分组;所有这些都可以通过任何操作简化为两个元素;但是,由于问题中不允许使用1,因此它们不可能减少到同质对(AABBCC)以下,所以我们知道这是我们在这种情况下可以做的最好的事情.

          总而言之,如果一个字符串是齐次的,我们就无能为力了;如果一个字符串是非齐次的并且在组中=A,则可以通过贪心算法将其简化为问题中的A,该算法在每一步都保持非齐次;如果组中的字符串=B=C 相同;最后,如果一个字符串是非同质的并且在组中=1,它可以通过一个尽可能长时间保持非同质的贪心算法减少到AABBCC之一。这些是我们可以通过操作的组属性来做的最好的事情。

          解决问题的程序:

          现在,既然我们知道了可能的结果,我们的程序可以在O(n) 时间运行如下:如果给定字符串中的所有字母都相同,则不可能减少,所以只输出字符串的长度。如果字符串是非齐次的,并且等于组中的标识,则输出数字2;否则输出数字 1。

          为了快速判断一个元素是否等于组中的同一性,我们使用交换性和结合性如下:只需将A's、B's 和C's 的数量计入变量@ 987654414@、bc。替换a = a mod 2b = b mod 2c = c mod 2,因为我们可以消除组中的AABBCC对。如果生成的abc 中没有一个等于 0,则我们在组中有 ABC=1,因此程序应该输出 2,因为不可能减少到标识 1。如果生成的abc 的所有三个都等于 0,我们再次具有标识(ABC 都取消了自己)所以我们应该输出 2 . 否则字符串是非恒等式的,我们应该输出 1。

          【讨论】:

            【解决方案12】:
            //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;
            
                }
            
            
            }
            

            }

            【讨论】:

              【解决方案13】:

              一次比较两个字符,如果两个相邻字符不相同则替换。要获得最佳解决方案,请从字符串的开头运行一次,从字符串的结尾运行一次。返回最小值。

              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” 但这段代码不会给出。由于我没有得到正确的评论位置(由于低分或任何其他原因)所以我在这里做了。

              【讨论】:

                【解决方案14】:

                这个问题可以通过贪心的方法来解决。尝试找到应用变换的最佳位置,直到不存在变换为止。最佳位置是转换后字符的不同邻居数最多的位置。

                【讨论】:

                  【解决方案15】:

                  您可以使用 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”上应用相同的内容以获得结果。 所以,一个是向前传球,一个是向后传球。

                  【讨论】:

                    【解决方案16】:
                            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;
                    

                    【讨论】:

                      【解决方案17】:
                      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;
                          } 
                      }
                      

                      }

                      【讨论】:

                      • 不适用于更大的字符串。 Stackoverflow 异常
                      【解决方案18】:

                      这是我的 C# 解决方案。

                      public static int StringReduction(string str)
                          {
                              if (str.Length == 1)
                                  return 1;
                              else
                              {
                                  int prevAns = str.Length;
                                  int newAns = 0;
                      
                                  while (prevAns != newAns)
                                  {
                                      prevAns = newAns;
                                      string ansStr = string.Empty;
                                      int i = 1;
                                      int j = 0;
                                      while (i < str.Length)
                                      {
                                          if (str[i] != str[j])
                                          {
                                              if (str[i] != 'a' && str[j] != 'a')
                                              {
                                                  ansStr += 'a';
                                              }
                                              else if (str[i] != 'b' && str[j] != 'b')
                                              {
                                                  ansStr += 'b';
                                              }
                                              else if (str[i] != 'c' && str[j] != 'c')
                                              {
                                                  ansStr += 'c';
                                              }
                      
                                              i += 2;
                                              j += 2;
                                          }
                                          else
                                          {
                                              ansStr += str[j];
                                              i++;
                                              j++;
                                          }
                                      }
                      
                                      if (j < str.Length)
                                      {
                                          ansStr += str[j];
                                      }
                      
                                      str = ansStr;
                                      newAns = ansStr.Length;
                                  }
                      
                                  return newAns;
                              }
                          }
                      

                      【讨论】:

                        猜你喜欢
                        • 2021-05-23
                        • 2023-03-22
                        • 1970-01-01
                        • 1970-01-01
                        • 2017-08-13
                        • 1970-01-01
                        • 2016-03-31
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多