目的: 求字符串a的每一个后缀字串 和 目标串b 的lcp(最长的公共前缀)
核心: DP思想, 更新 i 时 利用已知信息, 阴影 来处理, 在阴影里面 O1get, 阴影外面就更新阴影
思路:
- 利用 公共部分(跳过)来优化时间复杂度,
- 2个核心数组 nxt[] (b的lcp),extend(a的lcp)
- 一个关键元素 po (当前最长位置的开始地方)
- 利用这个当前最长位置,也就是这个阴影已经覆盖的地方, 在阴影里面就是O1处理, 不然就更新这个阴影, 这个阴影就是N长
- 时间复杂度就是这个阴影的长度 O(n); extend[i], 以i为开始和目标串 Lcp的最大匹配数(包含了自己的)
- nxt, 同理,不过是目标串的.
- 通过上面这个图更新i时看, 是否他的最大长度在这个阴影里面, 是就 O1处理, >= 就要对未知进行更新,
代码:
#include <bits/stdc++.h> using namespace std; #define ri register int #define M 20000007 int n,m; string s1,s2; int nxt[M],extend[M]; void getnxt() { int len=s2.length(); nxt[0]=len; int i=0; while(s2[i]==s2[i+1]&&i+1<len) i++;nxt[1]=i; int po=1; for(ri i=2;i<len;i++) { if(nxt[i-po]+i<nxt[po]+po) { nxt[i]=nxt[i-po]; } else { int j=po+nxt[po]-i; if(j<0) j=0; while(s2[i+j]==s2[j]&&i+j<len) j++;nxt[i]=j; po=i; } } } void getextend() { int l1=s1.length(),l2=s2.length(); int i=0; while(s1[i]==s2[i]&&i<l1&&i<l2) i++; extend[0]=i; int po=0; for(ri i=1;i<l1;i++) { if(nxt[i-po]+i<po+extend[po]) { extend[i]=nxt[i-po]; } else { int j=po+extend[po]-i; /// (po+extendp[po]-1-i+1) if(j<0) j=0; while(s1[i+j]==s2[j]&&j<l2&&i+j<l1) j++;extend[i]=j; po=i; } } } int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>s1>>s2; getnext(); getextend(); long long ans=0; for(ri i=0;i<s2.length();i++) { ans^=1ll*(i+1)*(nxt[i]+1); } cout<<ans<<endl; ans=0; for(ri i=0;i<s1.length();i++) { ans^=1ll*(i+1)*(extend[i]+1); } cout<<ans; return 0; }
后记:
- 记得对 po 更新!!
- 为什么是j++; .... 因为这个长度是包含了自己本身的
模板题: