Сокращение строки - Конкурс по программированию. Требуется решение - PullRequest
7 голосов
/ 18 декабря 2011

У меня есть вопрос, который просит нас сократить строку следующим образом.

Вводом является строка, имеющая только A, B или C. Выход должен быть длиной сокращенная строка

Строка может быть уменьшена по следующим правилам

Если любые две разные буквы соседствуют, эти две буквы могут быть заменяется третьей буквой.

Например, ABA -> CA -> B. Таким образом, окончательный ответ равен 1 (длина сокращенной строки)

Например ABCCCCCCC

Это не становится CCCCCCCC, так как может быть уменьшено альтернативно на

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

как здесь длина 2 <(длина <code>CCCCCCCC)

Как вы решаете эту проблему?

Большое спасибо!

Чтобы прояснить ситуацию: в вопросе говорится, что ему нужна минимальная длина сокращенной строки. Таким образом, во втором примере выше возможны 2 решения, одно CCCCCCCC, а другое AA. Таким образом, 2 является ответом, поскольку длина AA равна 2, что меньше, чем длина CCCCCCCC = 8.

Ответы [ 17 ]

7 голосов
/ 21 декабря 2011

То, как сформулирован этот вопрос, есть только три различные возможности:

  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.

6 голосов
/ 06 января 2012

Вы можете обобщить результат на основе количества символов в строке.Алгоритм выглядит следующим образом:

пройти через строку и получить индивидуальное количество символов.

Позволяет сказать, если

  • 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;
}
4 голосов
/ 18 декабря 2011

Я предполагаю, что вы ищете длину максимально короткой строки, которую можно получить после сокращения.

Простым решением было бы изучить все возможности жадным образом и надеяться, что оно не взорвется в геометрической прогрессии. Я напишу здесь псевдокод 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 должно быть просто отлично), добавляете в нее свою строку, а затем реализуете простой поиск в ширину во всех возможных сокращениях. Во время поиска вы берете первый элемент из очереди, берете все его возможные подстроки, выполняете все возможные сокращения и помещаете уменьшенные строки обратно в очередь. Все пространство исследуется, когда очередь становится пустой.

3 голосов
/ 22 декабря 2011

Давайте определим автомат со следующими правилами (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, могут быть уменьшены до двух букв.нам все еще нужно доказать, что входная строка не может быть уменьшена до одной буквы другой последовательностью сокращения.

2 голосов
/ 08 апреля 2012
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 голосов
/ 04 марта 2012

Сравните два символа за раз и замените, если оба соседних символа не совпадают.Чтобы получить оптимальное решение, запустите один раз от начала строки и один раз от конца строки.Вернуть минимальное значение.

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

}

1 голос
/ 11 мая 2015

Существует некоторая базовая структура, которая может быть использована для решения этой проблемы за O (n) времени.

Указанные правила являются (большей частью) правилами, определяющими математическую группу, в частности группу D_2, также известную иногда как K (для группы Клейна из четырех групп) или 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 является группой, она удовлетворяет ряду правил: замыкание (произведение любых двух элементов группы является другим элементом), ассоциативность (что означает (x*y)*z = x*(y*z) для любых элементов x, y, z, т. е. порядок уменьшения строк не имеет значения), существование тождества (существует элемент 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 недопустимы, поэтому мы не можем просто исключить пары AA, BB или CC. Однако ситуация не , что отличается. Рассмотрим строку ABB. Мы не можем написать ABB=A в этом случае. Однако мы можем устранить BB в два этапа, используя A: ABB=CB=A. Поскольку порядок операций не имеет значения по ассоциативности, мы гарантируем, что получим тот же результат. Поэтому мы не можем идти прямо от ABB до A, но мы можем получить тот же результат другим путем.

Такие альтернативные маршруты доступны, когда в строке есть хотя бы два разных элемента. В частности, в каждом из ABB, ACC, BAA, BCC, CAA, CBB, AAB, AAC, BBA, BBC, CCA, CCB, мы можем действовать так, как будто у нас есть уменьшение xx=1, а затем отбрасывать 1.

Отсюда следует, что любую строку, которая не является однородной (не все одна и та же буква) и имеет двухбуквенную подстроку (AA, BB или CC), можно уменьшить, удалив двойную букву. Строки, содержащие только две одинаковые буквы, не могут быть дополнительно сокращены (поскольку в задаче не допускается 1), поэтому можно с уверенностью предположить, что любая неоднородная строка может быть уменьшена до A, B , C, AA, BB, CC.

Однако мы все еще должны быть осторожны, потому что CCAACC можно превратить в CCCC, удалив среднюю пару AA, но это не лучшее, что мы можем сделать: CCAACC=AACC=CC or AA сводит нас к строка длиной 2.

Другая ситуация, с которой мы должны быть осторожны, это AABBBB Здесь мы можем исключить AA, чтобы закончить с BBBB, но лучше сначала убрать середину B, затем что угодно: AABBBB=AABB=AA or BB (оба из которых эквивалентны 1 в группе, но могут в дальнейшем проблема не сводится).

Есть еще одна интересная ситуация: AAAABBBB. Слепое устранение пар приводит нас к AAAA или BBBB, но мы могли бы добиться большего: AAAABBBB=AAACBBB=AABBBB=AABB=AA or BB.

Вышеприведенное указывает на то, что устранение двойных чисел вслепую не обязательно является способом продолжения, но, тем не менее, оно освещало.

Вместо этого кажется, что наиболее важным свойством строки является неоднородность.Если строка однородна, остановитесь, мы ничего не можем сделать.В противном случае идентифицируйте операцию, которая сохраняет свойство неоднородности, если это возможно.Я утверждаю, что всегда можно идентифицировать операцию, которая сохраняет неоднородность, если строка неоднородна и имеет длину четыре или более.

Доказательство: если 4-подстрока содержит две разные буквы, третья буква может быть введена на границе между двумя разными буквами, например, AABA переходит к ACA.Так как одна или две исходные буквы должны быть неизменными где-то внутри строки, из этого следует, что результат все еще является неоднородным.

Предположим, что вместо этого у нас есть 4-подстрока, которая имеет три различных элемента, скажем AABC, с двумя внешними элементами.Затем, если средние два элемента различны, выполните операцию над ними;результат неоднороден, потому что два самых внешних элемента все еще различны.С другой стороны, если два внутренних элемента одинаковы, например, ABBC, то они должны отличаться от обоих внешних элементов (в противном случае у нас было бы только два элемента в наборе из четырех, а не три).В этом случае выполните первую или третью операцию;что оставляет либо два последних элемента различными (например, ABBC=CBC), либо первые два элемента различными (например, ABBC=ABA), так что сохраняется неоднородность.

Наконец, рассмотрим случай, когда первый ипоследние элементы одинаковы.Тогда у нас такая ситуация, как ABCA.Два средних элемента должны отличаться от внешних элементов, в противном случае у нас было бы только два элемента, а не три.Мы можем взять первую доступную операцию, ABCA=CCA, и неоднородность снова сохранится.

Конец доказательства.

У нас есть жадный алгоритм для уменьшения любогонеоднородная строка длиной 4 или более: выберите первую операцию, которая сохраняет неоднородность;такая операция должна существовать по приведенному выше аргументу.

Теперь мы сократили до случая, когда мы имеем неоднородную строку из 3 элементов.Если два одинаковы, у нас либо есть двойные значения, такие как AAB и т. Д., Которые, как мы знаем, могут быть сведены к одному элементу, или у нас есть два элемента без двойных, как ABA=AC=B, которые также могут быть уменьшены до одного элементаили у нас есть три разных элемента, таких как ABC.Существует шесть перестановок, каждая из которых =1 в группе по ассоциативности и коммутативности;все они могут быть сведены к двум элементам с помощью любой операции;однако их нельзя уменьшить ниже однородной пары (AA, BB или CC), поскольку 1 не разрешено в задаче, поэтому мы знаем, что это лучшее, что мы можем сделать в этом случае.

Таким образом, если строка однородна, мы ничего не можем сделать;если строка неоднородна и =A в группе, она может быть уменьшена до A в задаче с помощью жадного алгоритма, который поддерживает неоднородность на каждом шаге;то же самое, если строка =B или =C в группе;наконец, если строка неоднородна и =1 в группе, ее можно уменьшить с помощью жадного алгоритма, который поддерживает неоднородность как можно дольше, до одного из AA, BB или CC.Это лучшее, что мы можем сделать с помощью групповых свойств операции.

Программа, решающая проблему:

Теперь, поскольку мы знаем возможные результаты, наша программа можетзапустите время O(n) следующим образом: если все буквы в данной строке одинаковы, сокращение невозможно, поэтому просто выведите длину строки.Если строка неоднородна и равна идентичности в группе, выведите число 2;в противном случае выведите число 1.

Чтобы быстро решить, равен ли элемент идентичности в группе, мы используем коммутативность и ассоциативность следующим образом: просто посчитаем число A, 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 голос
/ 05 января 2014

Согласно наблюдениям 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
1 голос
/ 08 апреля 2012

Это жадный подход, и обход пути начинается с каждой возможной пары и проверки минимальной длины.

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 голос
/ 25 января 2012
    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();
    }
}
...