Ищем подстроку в строке за линейное время.

F[n] - failure function, длина сдвига для подстроки с 0 символа до n-1
int m - длина подстроки
int n - длина строки
char* pattern - искомая подстрока
char* text - строка, в которой будем искать

Код:
int F[MAX];

void preKMP (char* pattern, int m){
    F[0]=F[1]=0;

    for (int i=2; i<=m; ++i){
        // j - индекс прошлого совпадения с префиксом
        int j=F[i-1];

        for (;;){
            // если данный символ i-1 расширяет данное совпадение j, то устанавливаем соответствующее значение в F[i-1]
            if(pattern[j]==pattern[i-1]){
                 F[i]=j+1;
                 break;
            }

            // если дошли до пустой строки
            if(j==0){
                 F[i]=0;
                 break;
            }
            
            // переходим к следующему совпадению
            j=F[j];
        }
    }
}

// возвращает индекс, с которого начинается первое полное совпадение

int KMP (char* pattern, int m, char* text, int n){
    preKMP(pattern, m);
    
    int j=0; // начальная позиция в подстроке
    int i=0; // начальная позиция в строке

    for (;;){
         // достигнут конец строки?
         if (i==n)
              break;
         
         // если данные символы строки и искомого образца совпадают....
         if (text[i]==pattern[j]){
              j++; // переходим к следующему символу образца
              i++; // и следующему символу строки
              if(j==m){ // нашли!
                   return i-m;
              }
         } else if(j>0){ // если можно перейти к другому префиксу, перейдем
              j=F[j];
         }
         else // если нельзя, переходим к следующему символу строки
             i++;
    }
    
    return -1;
}