BZOJ4503 两个串

给出文本串 S S 和模板串T T T 中可能有通配符,求S T T 所有出现位置。

1|T||S|100000

FFT神仙题。

在没有通配符的情况下,考虑两个长度为 n n 的串S,T 相等的充要条件是

i[0,n1]Si=Ti ∀ i ∈ [ 0 , n − 1 ] S i = T i

可以变形为

i[0,n1]SiTi=0 ∀ i ∈ [ 0 , n − 1 ] S i − T i = 0

自然的,我们想到

i=0n1SiTi=0 ∑ i = 0 n − 1 S i − T i = 0

但这并不能推出 S=T S = T ,所以我们将其修正为

i=0n1(SiTi)2=0 ∑ i = 0 n − 1 ( S i − T i ) 2 = 0

现在考虑通配符,如果 Ti T i 是一个通配符,我们就让它的值为 0 0 ,同时其他字符的值均不为0 ,那么可得

i=0n1Ti(SiTi)2=0 ∑ i = 0 n − 1 T i ( S i − T i ) 2 = 0

将式子展开,得

i=0n1S2iTi2SiT2i+T3i=0 ∑ i = 0 n − 1 S i 2 T i − 2 S i T i 2 + T i 3 = 0

T T <script type="math/tex" id="MathJax-Element-26">T</script> 翻转后对三项分别FFT即可

#include <bits/stdc++.h>

#define IL __inline__ __attribute__((always_inline))

#define For(i, a, b) for (int i = a, i##end = b; i <= i##end; ++ i)
#define FOR(i, a, b) for (int i = a, i##end = b; i < i##end; ++ i)
#define Rep(i, a, b) for (int i = a, i##end = b; i >= i##end; -- i)
#define REP(i, a, b) for (int i = a, i##end = b; i > i##end; -- i)

typedef long long LL;

template < class T >
IL T chkmax(T &a, const T b) {
  if (a < b)
    a = b;
  return a;
}

template < class T >
IL T chkmin(T &a, const T b) {
  if (a > b)
    a = b;
  return a;
}

template < class T >
IL T mymax(const T a, const T b) {
  return a > b ? a : b;
}

template < class T >
IL T mymin(const T a, const T b) {
  return a < b ? a : b;
}

template < class T >
IL T myabs(const T &a) {
  return a > 0 ? a : -a;
}

const int INF = 0X3F3F3F3F;
const double EPS = 1E-10, PI = acos(-1.0);

#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define OK DEBUG("Passing [%s] in LINE %d...\n", __FUNCTION__, __LINE__)
#define F first
#define S second
/*************************header*************************/

const int MAXN = 100000 + 5;

char s[MAXN], t[MAXN];
int len1, len2;

namespace Polynomial {
  const int MOD = 998244353, G = 3, Ginv = 332748118;

  IL int add(int a, int b) {
    a += b;
    return a >= MOD ? a - MOD : a < 0 ? a + MOD : a;
  }

  IL int mul(int a, int b) {
    return 1LL * a * b % MOD;
  }

  IL int FastPow(int a, int p) {
    int res = 1;
    for (int base = a; p; base = mul(base, base), p >>= 1)
      if (p & 1)
        res = mul(res, base);
    return res;
  }

  int pos[MAXN * 4], rev;

  IL void Init(int n) {
    FOR(i, 0, n)
      pos[i] = (pos[i >> 1] >> 1) | ((i & 1) << (rev - 1));
  }

  IL void NTT(int *a, int n, bool t) {
    FOR(i, 0, n)
      if (i > pos[i])
        std::swap(a[i], a[pos[i]]);
    for (int i = 2; i <= n; i <<= 1) {
      int m = i >> 1, w = FastPow(t ? G : Ginv, (MOD - 1) / i);
      for (int *p = a; p != a + n; p += i) {
        int o = 1;
        FOR(j, 0, m) {
          int tmp = mul(o, p[j + m]);
          p[j + m] = add(p[j], -tmp);
          p[j] = add(p[j], tmp);
          o = mul(o, w);
        }
      }
    }
  }

  IL void DFT(int *a, int n) {
    NTT(a, n, 1);
  }

  IL void IDFT(int *a, int n) {
    NTT(a, n, 0);
    int inv = FastPow(n, MOD - 2);
    FOR(i, 0, n)
      a[i] = mul(a[i], inv);
  }
}

int x[MAXN * 4], y[MAXN * 4], ans[MAXN * 4], pos[MAXN];

int main() {
  using namespace Polynomial;
  scanf("%s%s", s, t);
  len1 = strlen(s), len2 = strlen(t);
  std::reverse(t, t + len2);
  int sz = 1;
  for (; sz <= len1 + len2; sz <<= 1, ++ rev);
  Init(sz);
#define id(x) ((x) == '?' ? 0 : (x) - 'a' + 1)
  FOR(i, 0, len1)
    x[i] = id(s[i]) * id(s[i]);
  FOR(i, 0, len2)
    y[i] = id(t[i]);
  DFT(x, sz), DFT(y, sz);
  FOR(i, 0, sz)
    ans[i] = add(ans[i], mul(x[i], y[i]));
  memset(x, 0, sizeof x);
  memset(y, 0, sizeof y);
  FOR(i, 0, len1)
    x[i] = 1;
  FOR(i, 0, len2)
    y[i] = id(t[i]) * id(t[i]) * id(t[i]);
  DFT(x, sz), DFT(y, sz);
  FOR(i, 0, sz)
    ans[i] = add(ans[i], mul(x[i], y[i]));
  memset(x, 0, sizeof x);
  memset(y, 0, sizeof y);
  FOR(i, 0, len1)
    x[i] = id(s[i]);
  FOR(i, 0, len2)
    y[i] = id(t[i]) * id(t[i]);
  DFT(x, sz), DFT(y, sz);
  FOR(i, 0, sz)
    ans[i] = add(ans[i], mul(-2, mul(x[i], y[i])));
  IDFT(ans, sz);
  int cnt = 0;
  For(i, len2 - 1, len1 - 1)
    if (!ans[i])
      ::pos[++ cnt] = i - len2 + 1;
  printf("%d\n", cnt);
  For(i, 1, cnt)
    printf("%d\n", ::pos[i]);
  return 0;
}

这个关于字符串相等的转换特别奇妙。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值