P3763 [TJOI2017]DNA 题解
写一个比大部分做法跑得快的哈希(最优解 rk3)。
枚举 $S_0$ 子串的开头,二分找失配位置,在失配位置后二分找下一个失配位置,以此类推。
如果第 $4$ 个失配位置在子串外,那么这个子串符合要求。
考虑字符集大小只有 $4$,$\text{base}$ 取 $5$ 即可。
直接自然溢出啥事没有。
#include <stdio.h>
#include <string.h>
int T, n, m, l, q;
char a[100050], b[100050];
unsigned p[100050], f[100050], g[100050];
int main()
{
p[0] = 1;
scanf("%d", &T);
while (T--)
{
scanf("%s%s", a + 1, b + 1);
n = strlen(a + 1);
m = strlen(b + 1);
for (int k = m; l < k; ++l, p[l] = p[l - 1] * 5)
;
for (int i = 1; i <= n; ++i)
{
f[i] = f[i - 1] * 5;
switch (a[i])
{
case 'T':
f[i] += 1;
break;
case 'C':
f[i] += 2;
break;
case 'G':
f[i] += 3;
break;
}
}
for (int i = 1; i <= m; ++i)
{
g[i] = g[i - 1] * 5;
switch (b[i])
{
case 'T':
g[i] += 1;
break;
case 'C':
g[i] += 2;
break;
case 'G':
g[i] += 3;
break;
}
}
q = 0;
for (int i = 1; i + m - 1 <= n; ++i)
for (int j = 0, x = 0, l, r = 0; j < 4; ++j)
{
l = 0;
x += r + 1;
r = m - x + 1;
while (l <= r)
{
int m = l + r >> 1;
unsigned u = p[m];
if (f[i + x + m - 2] - f[i + x - 2] * u == g[x + m - 1] - g[x - 1] * u)
l = m + 1;
else
r = m - 1;
}
if (r == m - x + 1)
{
++q;
break;
}
}
printf("%d\n", q);
}
return 0;
}