Автор Тема: Сопоставление с образцом  (Прочитано 42213 раз)

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Сопоставление с образцом
« : Сентябрь 19, 2012, 04:12:19 am »
Продолжаем строить правильные циклы.
Дан образец, в котором звездочка (*) обозначает любое количество любых символов. Определить соответствует ли строка образцу. Пример:
"12345abc" соответствует "123*abc"
"12345abc*d*" не соответствует "123*abc"

Для определенности - строки это обычные строки (можно заглядывать вперед/назад). Все граничные условия учитываем (пустые строки и т.п.)

Рассуждаем про инварианты, формальные методы и т.д. Ждем info21 с циклом Дейкстры.

albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #1 : Сентябрь 19, 2012, 05:37:09 am »
А где же свой вариант под паролем? По сложившейся традиции надо бы :)

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Сопоставление с образцом
« Ответ #2 : Сентябрь 19, 2012, 09:33:09 am »
Я так понял, задача сводится к многократному поиску подстрок.

Если A, B, C, D, E,... - строки, то "*A*B*C*D*E*" - это значит сначала найти подстроку A, потом в оставшемся хвосте найти подстроку B, потом подстроку C и т. д.

Стало быть псевдокод цикла:
Пока не кончилась исходная строка для всех искомых подстрок
{
       найти эту подстроку;
}
Потом еще хвост надо проверить...

albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #3 : Сентябрь 19, 2012, 11:12:48 am »
Только вначале проверить и отбросить те края строки и образца, где у образца не *.
Тогда
Потом еще хвост надо проверить...
не нужно


albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #4 : Сентябрь 19, 2012, 11:17:57 am »
А как понимать * в проверяемой строке?
"12345abc*d*" не соответствует "123*abc"

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #5 : Сентябрь 19, 2012, 12:19:20 pm »
А что тут - задачка на составление КА по рег. выражению. И затем программирование КА. Если составите НКА, то будете программировать с откатом :) Если сделаете ДКА - то уложитесь в обычный цикл.

albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #6 : Сентябрь 19, 2012, 12:30:32 pm »
А как понимать * в проверяемой строке?
"12345abc*d*" не соответствует "123*abc"
Вопрос снимаю - потому что без разницы как.

valexey_u

  • Hero Member
  • *****
  • Сообщений: 3013
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #7 : Сентябрь 19, 2012, 01:22:39 pm »
А что тут - задачка на составление КА по рег. выражению. И затем программирование КА. Если составите НКА, то будете программировать с откатом :) Если сделаете ДКА - то уложитесь в обычный цикл.
Если сделаете НКА, то потом сведете к ДКА и все равно будет обычный цикл :-)
Y = λf.(λx.f (x x)) (λx.f (x x))

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #8 : Сентябрь 19, 2012, 03:42:26 pm »
А где же свой вариант под паролем? По сложившейся традиции надо бы :)

Чуть позже будет.

Berserker

  • Sr. Member
  • ****
  • Сообщений: 254
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #9 : Сентябрь 19, 2012, 07:16:59 pm »
Код: (delphi) [Выделить]
FUNCTION Match (CONST Str, Pattern: STRING): BOOLEAN;
CONST
  WILLCARD  = '*';

TYPE
  TState  =
  (
    STATE_STRICT_COMPARE,       // [L]
    STATE_FIRST_LETTER_SEARCH,  // *[L]
    STATE_MATCH_SUBSTR_TAIL,    // *L[...]*
    STATE_EXIT
  );

VAR
  State:          TState;
  StrLen:         INTEGER;
  PatternLen:     INTEGER;
  StrBasePos:     INTEGER;
  PatternBasePos: INTEGER;
  s:              INTEGER;  // Pos in Pattern
  p:              INTEGER;  // Pos in Str
 
  PROCEDURE SkipWillcards;
  BEGIN
    WHILE (p <= PatternLen) AND (Pattern[p] = WILLCARD) DO BEGIN
      INC(p);
    END; // .WHILE
  END; // .PROCEDURE SkipWillcards
 
  PROCEDURE SkipMatchingSubstr;
  BEGIN
    WHILE
      (p <= PatternLen)         AND
      (s <= StrLen)             AND
      (Pattern[p] <> WILLCARD)  AND
      (Str[s] = Pattern[p])
    DO BEGIN
      INC(p);
      INC(s);
    END; // .WHILE
  END; // .PROCEDURE SkipMatchingSubstr
 
  PROCEDURE Fail;
  BEGIN
    State   :=  STATE_EXIT;
    RESULT  :=  FALSE;
  END; // .PROCEDURE Fail
 
  PROCEDURE Success;
  BEGIN
    State   :=  STATE_EXIT;
    RESULT  :=  TRUE;
  END; // .PROCEDURE Success

BEGIN
  StrLen          :=  LENGTH(Str);
  PatternLen      :=  LENGTH(Pattern);
  StrBasePos      :=  1;
  PatternBasePos  :=  1;
  s               :=  1;
  p               :=  1;
  State           :=  STATE_STRICT_COMPARE;
  RESULT          :=  FALSE;
 
  WHILE State <> STATE_EXIT DO BEGIN
    CASE State OF
      STATE_STRICT_COMPARE:
        BEGIN
          SkipMatchingSubstr;
         
          IF (s > StrLen) AND (p > PatternLen) THEN BEGIN
            Success;
          END // .IF
          ELSE IF (s > StrLen) OR (p > PatternLen) OR (Pattern[p] <> WILLCARD) THEN BEGIN
            Fail;
          END // .ELSEIF
          ELSE BEGIN
            SkipWillcards;
            State :=  STATE_FIRST_LETTER_SEARCH;
          END; // .ELSE
        END; // .CASE STATE_STRICT_COMPARE
       
      STATE_FIRST_LETTER_SEARCH:
        BEGIN
          WHILE (s < StrLen) AND (Str[s] <> Pattern[p]) DO BEGIN
            INC(s);
          END; // .WHILE
         
          IF s > StrLen THEN BEGIN
            Fail;
          END // .IF
          ELSE BEGIN
            StrBasePos      :=  s;
            PatternBasePos  :=  p;
            INC(p);
            INC(s);
            State           :=  STATE_MATCH_SUBSTR_TAIL;
          END; // .ELSE
        END; // .CASE STATE_FIRST_LETTER_SEARCH
       
      STATE_MATCH_SUBSTR_TAIL:
        BEGIN
          SkipMatchingSubstr;
         
          IF (s > StrLen) AND (p > PatternLen) THEN BEGIN
            Success;
          END // .IF
          ELSE IF (s > StrLen) OR (p > PatternLen) THEN BEGIN
            Fail;
          END // .ELSEIF
          ELSE IF Pattern[p] <> WILLCARD THEN BEGIN
            p     :=  PatternBasePos + 1;
            s     :=  StrBasePos     + 1;
            State :=  STATE_FIRST_LETTER_SEARCH;
          END // .ELSEIF
          ELSE BEGIN
            SkipWillcards;
            State :=  STATE_FIRST_LETTER_SEARCH;
          END; // .ELSE
        END; // .CASE STATE_MATCH_SUBSTR_TAIL
    END; // .SWITCH State
  END; // .WHILE
 
  (* If "..." matches, "...*" matches too *)
  IF NOT RESULT THEN BEGIN
    SkipWillcards;
    RESULT  :=  (p > PatternLen) AND (s > StrLen);
  END; // .IF
END; // .FUNCTION Match

ShowMessage(IntToStr(ORD(Match('',            ''))));         // 1
ShowMessage(IntToStr(ORD(Match('a',           ''))));         // 0
ShowMessage(IntToStr(ORD(Match('',            'a'))));        // 0
ShowMessage(IntToStr(ORD(Match('',            '*'))));        // 1
ShowMessage(IntToStr(ORD(Match('aaa',         'bbb'))));      // 0
ShowMessage(IntToStr(ORD(Match('12345abc',    '123*abc'))));  // 1
ShowMessage(IntToStr(ORD(Match('12345abc*d*', '123*abc'))));  // 0
ShowMessage(IntToStr(ORD(Match('abceabcdef',  'abcd*f*'))));  // 0
ShowMessage(IntToStr(ORD(Match('abceabcdef',  '*abcd*f*')))); // 1
ShowMessage(IntToStr(ORD(Match('abceabcdef',  '*ab*d*f*')))); // 1

Таблица переходов:

strict_compare:
  end(Str) & end(Pattern)   =>  success
  end(Str) or end(Pattern)  =>  fail
  mismatch                  =>  fail
  match until willcard      =>  first_letter_search
 
first_letter_search:
  not found =>  fail
  found     =>  match_substr_tail
 
match_substr_tail:
  end(Str) & end(Pattern)   =>  success
  end(Str) or end(Pattern)  =>  fail
  match until willcard      =>  first_letter_search
  mismatch                  =>  match_substr_tail

exit:
  only willcards left to match  => success

Не судите строго.

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #10 : Сентябрь 19, 2012, 10:44:47 pm »
А где же свой вариант под паролем? По сложившейся традиции надо бы :)

Мой вариант. Редуцированный реальный код (на самом деле там сложнее образец):
public class star_expression
    {
    public static bool match( string s, string pattern )
        {
        var st = new state{ s = s, pattern = pattern };
        return match( ref st );
        }

    private struct state
        {
        public string s;
        public string pattern;
        public int string_pos;
        public int pattern_pos;
        }

    private static bool match( ref state st )
        {
        while ( st.pattern_pos < st.pattern.Length )
            {
            var ch = st.pattern[st.pattern_pos];
            if ( ch == '*' )
                {
                var try_state = new state{ s = st.s, pattern = st.pattern, string_pos = st.string_pos, pattern_pos = st.pattern_pos + 1 };
                if ( match( ref try_state ) )
                    return true;
                if ( st.string_pos == st.s.Length )
                    return false;
                ++st.string_pos;
                }
            else if ( st.string_pos < st.s.Length && ch == st.s[st.string_pos] )
                {
                ++st.pattern_pos;
                ++st.string_pos;
                }
            else
                return false;
            }
           
        return st.string_pos == st.s.Length;
        }
    }

Набор тестовых данных:
Assert.That( star_expression.match( "str123", "str123*" ), Is.True );
Assert.That( star_expression.match( "str123", "str*" ), Is.True );
Assert.That( star_expression.match( "str123", "*str123" ), Is.True );
Assert.That( star_expression.match( "str123", "*123" ), Is.True );
Assert.That( star_expression.match( "str123", "*str123*" ), Is.True );
Assert.That( star_expression.match( "str123", "*r1*" ), Is.True );
Assert.That( star_expression.match( "str123", "*s*r1*3" ), Is.True );
Assert.That( star_expression.match( "str123", "*s*r1*34" ), Is.False );
Assert.That( star_expression.match( "str123", "*" ), Is.True );
Assert.That( star_expression.match( "str123", "**" ), Is.True );
Assert.That( star_expression.match( "str123", "***" ), Is.True );
Assert.That( star_expression.match( "", "" ), Is.True );
Assert.That( star_expression.match( "str123", "" ), Is.False );
Assert.That( star_expression.match( "", "*" ), Is.True );

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #11 : Сентябрь 19, 2012, 10:52:14 pm »
А что тут - задачка на составление КА по рег. выражению. И затем программирование КА.

Как бы да. Я всегда начинаю такие задачки с автомата. Но в процессе явные состояния со свитчом почему-то всегда редуцируются до вызова отдельных функций (см. мой пример).

Если составите НКА, то будете программировать с откатом :) Если сделаете ДКА - то уложитесь в обычный цикл.

Боюсь показаться серым, но не могу придумать как тут обойтись без откатов (рекурсии)?

P.S. Да, была идея сначала странслировать все это в обычное регулярное выражение и там стандартное библиотченое решение использовать. Но образец немного сложнее, чем я описал. Поэтому решил врукопашную.

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #12 : Сентябрь 20, 2012, 01:58:55 am »
Не судите строго.

Не увидел в твоем коде откатов. Будет ли он работать для таких данных:
abcdefgabcefg -> abc*efg
abcdefgabcefg -> abc*efg*efg

albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #13 : Сентябрь 20, 2012, 04:13:55 am »
Зачем в этой задаче откаты?
В общем виде всё сводится к:
1. Сначала проверить совпадение подстрок в фиксированных местах, а это только может быть на концах строки.
2.Затем проверить наличие остальных подстрок образца по первому же совпадения в оставшейся части проверяемой строки.
 

vlad

  • Hero Member
  • *****
  • Сообщений: 1391
    • Просмотр профиля
Re: Сопоставление с образцом
« Ответ #14 : Сентябрь 20, 2012, 04:50:54 am »
Зачем в этой задаче откаты?
В общем виде всё сводится к:

Не очень понял. Код в студию ;) Вариант Berserker'а не осилил. Но попробую еще раз, если у него работают предложенные варианты.