zoukankan      html  css  js  c++  java
  • 【做题】BZOJ2534 L-gap字符串——调和级数

    题意:给出一个字符串,问其中有多少个子串恰好为(uvu)的形式。其中,(u)非空,(v)的长度恰好为(l)

    (n leq 5 imes 10^4)

    我们设两个后缀的起点分别为(a,b \, (a < b)​),那么,它们能贡献一个合法的子串当且仅当(lcp(a,b) + l geq b - a​)。这样,我们得到了(O(n^2)​)的暴力。

    由于(lcp(a,b))(b-a)没有什么关系,所以我们考虑枚举(b-a)。设(b-a = s)。那么,答案中(u)的长度就要满足(|u| geq s - l)。因此,我们可以忽视长度小于(s-l)的字符串。

    考虑这样一种做法:枚举(s-l),并且把字符串分成(leftlceil frac {n} {s-l} ight ceil)段,于是产生(O left(frac {n} {s-l} ight))个端点。我们发现,所有满足(|u| geq s-l)的子串(u)都至少覆盖一个端点。因此,我们枚举所有端点,并计算所有覆盖它的子串对答案的贡献。为了避免重复计算,我们只在一个子串覆盖的最右边的端点计算它的贡献。

    现在,我们已有一个端点(p),那么另一个(u)的起点就在(p + s)。那么,只要求出(lcp(p,p+s))(lcs(p,p+s)),我们就能得出所有以(p)为其覆盖的最后一个端点的可能的(u)的数量。

    因为(H_n = ln n + O(1)),所以如果使用(O(1))的lcs和lcp,时间复杂度是(O(n log n))的。(但博主写了(O(n log^2 n))

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 80010, BAS = 233;
    typedef unsigned long long ull;
    ull has[N],mul[N];
    long long ans;
    int n,l;
    char s[N];
    ull gethas(int a,int b) {
      return has[b] - has[a-1] * mul[b-a+1];
    }
    bool check(int a,int b,int c,int d) {
      return gethas(a,b) == gethas(c,d);
    }
    int lcs(int a,int b) {
      int l = 0, r = min(a,b), ret = 0, mid;
      while (l <= r) {
        mid = (l + r) >> 1;
        if (check(a-mid+1,a,b-mid+1,b)) l = mid + 1, ret = mid;
        else r = mid - 1;
      }
      return ret;
    }
    int lcp(int a,int b) {
      int l = 0, r = min(n - a + 1, n - b + 1), ret = 0, mid;
      while (l <= r) {
        mid = (l + r) >> 1;
        if (check(a,a+mid-1,b,b+mid-1)) l = mid + 1, ret = mid;
        else r = mid - 1;
      }
      return ret;
    }
    int main() {
      scanf("%d",&l);
      scanf("%s",s+1);
      n = strlen(s+1);
      for (int i = 1 ; i <= n ; ++ i)
        has[i] = has[i-1] * BAS + s[i];
      mul[0] = 1;
      for (int i = 1 ; i <= n ; ++ i)
        mul[i] = mul[i-1] * BAS;
      for (int s = 1 ; s <= n ; ++ s) {
        for (int i = 1, j = 1 + s + l, a, b ; j <= n ; i += s, j += s) {
          a = lcs(i,j);
          b = lcp(i,j);
          if (b > s) continue;
          ans += max(0,a + b - s);
        }
      }
      printf("%lld
    ",ans);
      return 0;
    }
    

    小结:一些基本套路都不会……字符串能力有待提高。

  • 相关阅读:
    P3180 [HAOI2016]地图
    P2787 语文1(chin1)- 理理思维
    P2221 [HAOI2012]高速公路
    P4137 Rmq Problem / mex
    P3746 [六省联考2017]组合数问题
    P2461 [SDOI2008]递归数列
    P3715 [BJOI2017]魔法咒语
    P3195 [HNOI2008]玩具装箱TOY
    Linux下的strerror是否线程安全?
    bash/shell的字符串trim实现
  • 原文地址:https://www.cnblogs.com/cly-none/p/9395848.html
Copyright © 2011-2022 走看看