kmp、差分约束
题意:
分析
第一个要突破的点在于:如何表示认错树木的现象。
我们可以暴力地枚举记忆中的p字符串,然后再用kmp进行匹配
但是很明显这会超时!
所以我们需要采取其他的办法。进行分辨。
这里我们使用的技巧叫做差分约束!
通过记录当前p[i]出现的位置与其上一次出现位置的差值,来限制数目的种类。
记录前后出现的差值。第一次出现则为0
然后在这种情况下进行kmp匹配.得出答案。
但是,差分约束也有一个问题:
如:
s = [0,0,2,2,2,2]
p = [0,0,2,2]
我们可以发现很明显s[0:4]==p[0:4]
但是其实s[2:6]==p[0:4]
因为,根据s[4]==2(索引从0开始)
s[2]==s[4]同理s[3]==s[5]
而p[0]=p[2],p[1]==p[3]
因此,是匹配的!!!
这种情况我们怎么区分呢?
我们发现,其实特殊的只有s[2]和s[3]
即第一次出现的时候,这是理所当然的。之后出现的都是与之前出现的差值。所以匹配条件一定是s[i]==p[j]
但是,第一次出现时应进行特殊判断!
其实,我们所要哦判断的就是,对于s[i]!=0的一个数。
我们如何判断在我们以匹配的p[0:j]中
他代表的字符是第一次出现还是已经出现过了。
其实只需判断s[i]是否大于j就可以了。
因为如果大于j那么意味着,s[i]与其上一次出现的距离超过了已经匹配的长度。
那么自然时第一次出现了。这时我们只要看p[j]是否为0.因为此时s[i]就相当于0啊
故,kmp算法中的判断条件不再是s[i]==p[j]而是s[i]<=j?s[i]==p[j]:p[j]==0
getnext也是一样的
如此,此题得解
代码如下
#include<iostream> #include<algorithm> using namespace std; const int max_n = 2e5 + 100; const int max_m = 1e5 + 100; const int max_k = 12; inline int read() { char c = getchar(); int x = 0, f = 1; while (!isdigit(c)) { if (c == '-')f = -1;c = getchar(); } while (isdigit(c))x = (x << 3) + (x << 1) + (c ^ 48), c = getchar(); return x * f; } inline void write(int x) { if (x < 0) { putchar('-'); x = -x; } if (x > 9) write(x / 10); putchar(x % 10 + '0'); return; } int n, k, m; int s[max_n]; int p[max_m]; int h[max_k]; int net[max_n]; inline void getnext(int p[],int lp) { int i = 0;int j = -1; net[0] = -1; while (i < lp) { if (j == -1 || p[i] <= j ? p[i] == p[j] : p[j] == 0) net[++i] = ++j; else j = net[j]; } } inline int kmp(int s[],int ls,int p[],int lp) { int res = 0; int i = 0;int j = 0; while (i < ls) { if (j == -1 || s[i] <= j ? s[i] == p[j] : p[j] == 0) { ++i; ++j; } else j = net[j]; if (j == lp) { j = net[j]; ++res; } }return res; } int main() { n = read();k = read(); for (register int i = 0;i < n;++i)s[i] = read(); m = read(); for (register int i = 0;i < m;++i)p[i] = read(); for (register int i = 0;i < k;++i)h[i] = -1; for (register int i = 0;i < n;++i) { if (h[s[i]] == -1) { h[s[i]] = i; s[i] = 0; } else { int tmp = s[i]; s[i] = i - h[s[i]]; h[tmp] = i; } } for (register int i = 0;i < k;++i)h[i] = -1; for (register int i = 0;i < m;++i) { if (h[p[i]] == -1) { h[p[i]] = i; p[i] = 0; } else { int tmp = p[i]; p[i] = i - h[p[i]]; h[tmp] = i; } } getnext(p, m); write(kmp(s, n, p, m)); }