:: алгоритмы  и методы :: :: олимпиадные задачи :: :: связь :: :: о сайте ::
Путь: Поиск. Строки и последовательности » Точный подстроки в строке » Турбо - обращенние сегмента
  Турбо - обращенние сегмента



Автор: Thierry Lecroq
Перевод с английского - Кантор И.

     Этот алгоритм является улучшением 'алгоритма обращенного сегмента' уменьшающим наибольшее число сравнений до 2*n. В самом деле, достаточно запомнить префикс u (x), который совпал во время прошлой попытки. Тогда во время текущей попытки при достижении правого конца u, легко доказать, что достаточно прочитать заново не более, чем правую половину u. Это и заложено в алгоритме турбо-обращения сегмента.

     Пусть слово z - сегмент слова w.
     Oпределим смещение disp( z, w) - от англ. displacement как положительное целое число, такое что

w [ m - d - |z| - 1 , m - d ] = z.


     Типичная для TRF-алгоритма ( Turbo Reverse Factor - о чем мы говорим) ситуация возникает, когда префикс u был найден при прошлой попытке, а сейчас алгоритм пытается сравнить сегмент v длины m - |u| с текстом сразу справа за u. Точка между u и v называется 'точкой принятия решения'. Если v не является сегментом x, то сдвиг вычисляется, как в простом алгоритме обращенного сегмента.

     Если v - суффикс x, тогда мы нашли x, а если v - не суффикс, но сегмент x, то достаточно просмотреть заново min( per( u ) , |u| / 2 ) правых символов u. Если u - периодичное ( т.е per( u ) <= |u| / 2 ), то пусть z - суффикс u длины per( u ). По определению периода, z - непериодичное слово, а значит следующее наложение невозможно:

Иллюстрация 1


     Таким образом, z может появиться в u лишь на расстоянии, кратном per( u ), и следовательно, наименьший подходяший суффикс uv, являющийся префиксом x имеет длину |uv| - disp( zv , x ) = m - disp( zv , x ), значит, длину сдвига можно положить равной disp( zv , x ).

     Если u - не периодичное: per( u ) > |u| / 2, то очевидно, что x не может появиться второй раз в левой части u длины per( u ). А значит, достаточно будет просмотреть правую часть u длины |u| - per( u ) < |u| / 2, чтобы найти неопределенный переход автомата. Функция disp реализована непосредственно в автомате S( x ) без увеличения сложности его построения.

     Турбо алгоритм обращения сегмента производит в худшем случае 2*n сравнений и в среднем оптимален.


  Реализация на Си



  struct Arrow {
   int target, shift;
};

struct State {
   int length, suf, pos;
   char terminal;
};

typedef struct Arrow ARROW;
typedef struct State STATE;

int state_counter;
int next[ XSIZE + 1 ];
/* Creates and returns a clone of state p. */
int COPY ( int p , STATE *states , ARROW trans[ XSIZE ][ ASIZE ])
{
 int r;
 
 r = state_counter++;
 memcpy( trans[ r ] , trans[ p ] , ASIZE * sizeof( ARROW ));
 states[ r ].suf = states[ p ].suf;
 states[ r ].pos = states[ p ].pos;
 return (r);
}

/* Creates the suffix automation for the reverse of the word x and */
/*  returns its initial state.                              */
int SUFF( char *x, int m, STATE *states, ARROW trans[ XSIZE ][ ASIZE ])
{
 int i, art, init, last, p, q, r;
 char a;
 
 art = state_counter++;
 init = state_counter++;
 states[ init ].suf = art;
 last = init;
 for ( i = m - 1; i >= 0; --i) {
    a = x[ i ];
   p = last;
   q = state_counter++;
   states[ q ].length = states[ p ].length + 1;
   states[ q ].pos = states[ p ].pos + 1;
   while ( p != init && trans[ p ][ a ].target == 0 ) {
      trans[ p ][ a ].target = q;
      trans[ p ][ a ].shift = states[ q ].pos - states[ p ].pos - 1;
      p = states[ p ].suf;
   }
   if ( trans[ p ][ a ].target == 0 ) {
      trans[ init ][ a ].target = q;
      trans[ init ][ a ].shift = states[ q ].pos - 
	                                         states[ init ].pos - 1;
      states[ q ].suf = init;
   }
   else
     if ( states[ p ].length + 1 == 
	                         states[ trans[ p ][ a ].target ].length)
        states[ q ].suf = trans[ p ][ a ].target;
     else {
 r = COPY( trans[ p ][ a ].target , states, trans );
 states[ r ].length = states[ p ].length + 1;
 states[ trans[ p ][ a ].target ].suf = r;
 states[ q ].suf = r;
 while ( p != art && states[ trans[ p ][ a ].target ].length >=
                                               states[ r ].length) {
    trans[ p ][ a ].shift = states[ trans[ p ][ a ].target ].pos - 
	                                            states[ p ].pos - 1;
   trans[ p ][ a ].target = r;
   p = states[ p ].suf;
 }
    }
   last = q;
 }
 states[ last ].terminal = 1;
 p = last;
 while ( p != init ) {
    p = states[ p ].suf;
   states[ p ].terminal = 1;
 }
 return ( init );
}

/* Computation of the Morris and Pratt next function */
int MP( char *x, int m, int mp_next[])
{
 int i, j;
 
 i = 1;
 j = 0;
 mp_next[ 0 ]= -1;
 while ( i < m )
    if ( j > -1 && x[ i ] != x[ j ])
      if ( j ) j = mp_next[ j-1 ] + 1;
      else j = -1;
   else mp_next[ i++ ] = j++;
 for ( i = 0; i < m; ++i ) mp_next[ i ] = i - mp_next[ i ];
 return 0;
}

/* Turbo Reverse Factor algorithm. */
void TRF( char *y, char *x, int n, int m)
{
 int period, i, j, shift, u, period_of_u, disp;
 int init, state, state_aux, mu;
 STATE states[ 2 * XSIZE + 2 ];
 ARROW trans[ XSIZE ][ ASIZE ];
 
 /* Preprocessing */
 memset( trans, 0, 2 * ( m + 2 ) * ASIZE * sizeof ( ARROW ) );
 memset( states, 0, 2 * ( m + 2 ) * sizeof( STATE ) );
 state_counter = 1;
 init = SUFF( x , m , states , trans );
 MP ( x, m, next );
 period = next [ m - 1 ];
 i = period_of_u = 0;
 shift = m;
 
 /* Searching */
 while ( i <= n - m ) {
    j = m - 1;
   state = init;
   u = m - 1 - shift;
   shift = m;
   disp = 0;
   while (j>u && (state_aux=trans[state][y[i+j]].target)!=0) {
      disp += trans[ state ][ y[ i+j ] ].shift;
      state = state_aux;
      if ( states[ state ].terminal ) shift = j;
      j--;
   }
   if ( j > u ) period_of_u = (shift != m ? next[m-1-shift]:0);
   else if ( !disp ) {
      OUTPUT (i);
      shift = period;
      period_of_u = ( shift != m ? next[ m - 1 - period ] : 0 );
   }
   else {
      mu = ( u + 1 ) / 2;
      if ( period_of_u <= mu ) {
         u -= period_of_u;
         while (j>u&&(state_aux=trans[state][y[i+j]].target)!=0) {
            disp += trans[ state ][ y[ i+j ] ].shift;
            state = state_aux;
            if ( states[ state ].terminal ) shift = j;
            j--;
         }
         if (j>u) period_of_u = (shift!=m ? next[m-1-shift] : 0);
            else {
               shift = disp;
               period_of_u = next[ m - 1 - disp ];
            }
         }
         else {
            u -= mu;
            --u;
            while (j>u&&(state_aux=trans[state][y[i+j]].target)!=0) {
               disp += trans[ state ][ y[ i+j ] ].shift;
               state=state_aux;
               if ( states[ state ].terminal ) shift = j;
               j--;
            }
            period_of_u = ( shift != m ? next[ m - 1 - shift ] : 0 );
         }
      }
      i+=shift;
   }
}