• 【做题】CF119D. String Transformation——KMP


    题意:有两个字符串(a,b),下标从(0)开始。求数对((i,j))满足(a[i+1:j] + r(a[j:n]) + r(a[0:i+1]) = b),其中(r(s))表示字符串(s)的反串。若有多组解,输出其中(i)最大,然后(j)尽可能小的一组。

    (|a|,|b| leq 10^6)

    首先考虑枚举(i)。那么,我们就要让(a[i+1:j] + r(a[j:n]) = b[0:n-i-1])。因此,前面的(a[i+1:j])必须是(b)的一个前缀。这个限制比较简单,求出最长公共前缀后,就可以转化为(j leq r)的形式。

    接下来,我们得满足(r(a[j:n]))(b[0:n-i-1])的一个后缀。并且,我们只要求出满足这个条件的最小的(j)就可以了。也就是求出(a)最长的后缀,它在翻转后也是(b[0:n-i-1])的后缀。这个问题比较复杂,要进行化简。先解决翻转。记(a_r)(a)的反串,那么,问题就成为求最长的(a_r)的前缀,它也是(b[0:n-i-1])的后缀。开始于一个固定位置的前缀,结束于多个不同位置的后缀。仔细一想的话,这正是KMP中nex数组的定义。因此,我们把(a_r)(b)连起来(分隔符还是要加的),求一遍nex数组就可以了。

    顺便一提,最长公共前缀必须用EXKMP来求,不然会T。

    时间复杂度(O(n))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1000010;
    char a[N],b[N],s[N << 1];
    int nex[N << 1],n,pre[N << 1],pw[N],ans1,ans2;
    int main() {
      cin.getline(a+1,N);
      cin.getline(b+1,N);
      if (strlen(a+1) != strlen(b+1)) {
        puts("-1 -1");
        return 0;
      }
      n = strlen(a+1);
      for (int i = 1 ; i <= n ; ++ i)
        s[i] = a[n - i + 1];
      s[n+1] = 0;
      for (int i = 1 ; i <= n ; ++ i)
        s[i + n + 1] = b[i];
      nex[0] = -1;
      for (int i = 1, j = -1 ; i <= (n << 1) + 1 ; nex[i++] = ++ j)
        while (j >= 0 && s[i] != s[j+1]) j = nex[j];
      for (int i = 1 ; i <= n ; ++ i)
        s[i] = b[i];
      s[n+1] = 0;
      for (int i = 1 ; i <= n ; ++ i)
        s[i + n + 1] = a[i];
      pre[1] = 2 * n + 1;
      while (s[2 + pre[2]] == s[1 + pre[2]] && 2 + pre[2] <= 2 * n + 1)
        ++ pre[2];
      int p = 2;
      for (int i = 3 ; i <= 2 * n + 1 ; ++ i) {
        if (pre[i - p + 1] < pre[p] + p - i)
          pre[i] = pre[i - p + 1];
        else {
          int j = max(0,p + pre[p] - i);
          while (s[i + j] == s[1 + j] && i + j <= 2 * n + 1)
    				++ j;
          pre[i] = j;
          p = i;
        }
      }
      ans1 = ans2 = -1;
      for (int i = 1 ; i <= n ; ++ i) {
        int r = pre[i + n + 1] + i;
        int l = nex[2 * n + 1 - (i-1)];
        if (n - l + 1 <= r) ans1 = i - 2, ans2 = n - l;
        if (a[i] != b[n-i+1]) break;
      }
      if (ans1 == -1) ans2 = -1;
      printf("%d %d
    ",ans1,ans2);
      return 0;
    }
    
    

    小结:这道题难度其实不大,但推导过程较长,因此需要时刻保持思路清晰,心态平稳。

  • 相关阅读:
    3--OC -- 点语法
    2--OC -- 类的创建与实例化
    1--OC -- HelloWorld
    tags,模板继承,组件,静态文件设置
    Django-过滤器的参数和语法
    Django- filter和simpletag,inclusion_tag的用法
    DjangoORM属性操作和models创建类语法
    Django项目的基础配置
    网络编程
    面试题
  • 原文地址:https://www.cnblogs.com/cly-none/p/9627702.html
Copyright © 2020-2023  润新知