字符串杂选
考虑 \(KMP\) 的 \(nxt\) 数组。
发现答案即 \(n-next[n]\) ,输出即可。
$code$
#include <bits/stdc++.h>
#define ll long long
using std::cin; using std::cout; using std::cerr;
const int N=1e6+10;
int nxt[N];
char a[N];
int n;
inline void getnxt(void){
int j=0;
for(int i=2;i<=n;++i){
while(j&&a[i]!=a[j+1]) j=nxt[j];
if(a[i]==a[j+1]) ++j;
nxt[i]=j;
}
}
int main(){
cin>>n;
cin>>a+1;
getnxt();
cout<<n-nxt[n];
return 0;
}
考虑到这道题又要求拼接,而且又出现前缀。所以继续考虑 \(kmp\) 。
但是这题还要求长度最大周期,也就是剩下的一段最小,所以可以考虑直接求一个 \(nxt'\) 数组。那还是同样的道理。但是你会发现 \(j\) 通常是 \(nxt[i]\) 。所以直接可以推导出一个式子,大概就长成这副模样:
但是有些时候是不会被计算的,因为这个串并不会存在一个周期,这个时候这个串肯定会 \(nxt'[i] < \left[\dfrac{i}{2}\right]\). 然后这种情况我就不更新 \(ans\) 就好。
因为数据范围高达 \(10^6\) 所以最大的 \(ans\) 也就是 \({10^6} ^2=10^{12} .\) 需要 \(\text{long long}\) 不然会傻乎乎地挂掉。
$code$
#include <bits/stdc++.h>
#define ll long long
#define int long long
using std::cin; using std::cout; using std::cerr;
const int N=1e6+10;
int nxt[N],fail[N];
char a[N];
int n,ans;
inline void getnxt(void){
int j=0;
for(int i=2;i<=n;++i){
while(j&&a[j+1]!=a[i]) j=fail[j];
if(a[i]==a[j+1]) ++j;
fail[i]=j;
}
j=0;
for(int i=2;i<=n;++i){
j=fail[i];
nxt[i]=(nxt[j]==0?fail[i]:nxt[j]);
}
}
signed main(){
cin>>n>>a+1;
getnxt();
for(int i=2;i<=n;++i){
if(i-nxt[i]>=ceil(i/2.0) && nxt[i])
ans+=i-nxt[i];
}
return cout<<ans<<"\n",0;
}
彬哥甚至可以想出这个题的思路。虽然他好像不是很会写 \(\text{kmp.}\)
题意:添加最少的字符使整个串回文起来。
那直接计算最长的可以接触到整个串末尾位置就好了。也就是 \(i+r[i]-1 = n.\)
你看这个题目标题也应该知道这题目的做法了吧。 ——周老
$code$
#include <bits/stdc++.h>
#define ll long long
#define f(x) (x<<1|1)
using std::cin;
using std::cout;
const int N=2.2e7+10;
int r[N],a[N],b[N];
int center,limit,ans;
int n;
char ch[N];
int main(){
cin>>n;
cin>>(ch+1);
for(int i=1;i<=n;++i)
b[i]=ch[i]+1;
for(int i=1;i<=n;++i)
a[2*i-1]=200,a[2*i]=b[i];
a[n<<1|1]=200;
for(int i=1;i<=(n<<1|1);++i){
if(i<=limit) r[i]=std::min(limit-i,r[2*center-i]);
while(i-r[i]>=1&&a[i-r[i]]==a[i+r[i]]) ++r[i];
if(i+r[i]>limit) center=i,limit=i+r[i];
}
for(int i=(n<<1|1);i>=1;--i){
if(i+r[i]-1==f(n)) ans=f(n)-(f(n)-i)*2-1;
}
cout<<ans/2<<'\n';
}
先用 \(dfs\) 跑一边整棵树的所有节点到根节点的异或和。考虑到一个点 \(u\) 到一个点 \(v\) 的异或和 \(=\) \(u\) 到根节点的异或和 \(\oplus\) \(v\) 到根节点的异或和,所以最大化两个点 \(s[i] \oplus s[j] .\)
然后对于每个 \(s[i]\) 找到一个 \(s[j]\) 使 \(s[i] \oplus s[j]\)在最大化。不显然这个是可以在字典树上面跑的。
先把所以 \(s[i]\) 挂到字典树上面。也就是二进制串挂上去。
贪心。如果字典树上面存在 \(s[i]\) 该位置不同的值那就往那个值身上跑,否则只能去跑相同的值。显然高位的 \(1\) 比所有低位的 \(1\) 叠加起来都要更大,所以贪心成立。
最后去找所有 \(s[i]\) 答案中的最大值就好了,时间复杂度是 \(\mathcal{O(n \log_2\alpha)}\) 被时间所允许。应该没有更加优秀的做法了吧。
细节:要添加前导零。
$code$
// Qx 's data
#include <bits/stdc++.h>
#define int long long
#define fir first
#define sec second
#define add push_back
using std::cin;
using std::cout;
using std::cerr;
const int N=1e6+10,limit=35;
struct Node{
int num,son[2];
}T[N];
int cnt,n,m;
void Insert(std::vector<int> s){
int now=0;
for(int ch:s){
if(!T[now].son[ch]) T[now].son[ch]=++cnt;
now=T[now].son[ch], T[now].num++;
}
}
std::vector<int> Find(std::vector<int> s){
int now=0;
std::vector<int> res;
for(int ch:s){
if(T[now].son[ch^1]) res.add(1), now=T[now].son[ch^1];
else res.add(0), now=T[now].son[ch];
}
return res;
}
int ans;
int s[N],a[N];
std::vector<std::pair<int,int> > G[N];
void dfs(int u,int f){ for(std::pair<int,int>pr:G[u]) if(pr.fir^f) s[pr.fir]=pr.sec^s[u], dfs(pr.fir,u); }
inline void to2(std::vector<int>& vec,int x){
int num1=x,ret=0;
while(x) vec.push_back(x&1), x>>=1;
while(num1) ret++,num1>>=1;
int p=limit-ret;
while(p--) vec.add(0);
reverse(vec.begin(),vec.end());
}
int to10(std::vector<int> vec){
reverse(vec.begin(),vec.end());
int answer=0,base=1;
for(int x:vec) answer+=x*base,base<<=1;
return answer;
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<n;++i){
int opu,opv,opw;
scanf("%lld%lld%lld",&opu,&opv,&opw), G[opu].add({opv,opw}), G[opv].add({opu,opw});
}
dfs(1,0);
for(int i=1;i<=n;++i){
std::vector<int> tmp;
tmp.clear();
to2(tmp,s[i]);
Insert(tmp);
}
for(int i=1;i<=n;++i){
std::vector<int> tmp;
to2(tmp,s[i]), ans=std::max(ans,to10(Find(tmp)));
}
return printf("%lld\n",ans),0;
}
\(T8\) 断环成链然后后缀数组跑一跑就完事。后缀数组的板子还是很简单的,反正我都是 \(\mathcal{O(n \log_2 ^2 n)} .\)