【问题描述】
学不会最小回文串划分的豆豆决定弃疗选择挑战非回文划分。
他想知道一个字符串 S 的最少和最多能划分成几个非回文串?
注: 如果一个字符串不是回文串,那么他是非回文串。例如()()是非回文串,而())(是
回文串。
【输入格式】
第一行一个整数 T,表示测试数据组数。
接下来 T 行,每行一个仅由小写字母构成的字符串 S。
【输出格式】
输出 T 行,每行两个整数分别表示最小和最大划分。如果不存在非合法划分输出“-1 -1”
(不含分号)。
【输入样例】
3
aaa
abba
abcb
【输出样例】
-1 -1
2 2
1 2
10%:
爱咋暴力咋暴力,注意常数
30%:
O(N^2)
建立一棵回文树,然后暴力转移 DP 即可。
60%:
O(NlogN)
维护方法类似于回文串划分,利用border的特点分成log段等差数列维护DP转移。
估计没人这么写。
100%:
O(N) 贪心+分析结论
结论 1:
最小划分的答案只能是 1 或者 2 或者无解。
通过简单讨论和构造即可证明。
结论 2:
先考虑一个答案上界,我们贪心从左往右划分,得到一个答案 X 。(如果最后
余下一个字母忽略就好了)
那么这个值只会在一种情况下比正确答案大 1,如下:
A b A b A b A b A b A
A 均是同一种字母,b 是一种于 A 不同的字母(b 之间可能互补相同)。
然后特判一下就好了。
然后无解情况如下:
$aaaaaaaaa$
$abababababa$///注意还有一种情况也不行:aaabaaa,是吧,标准答案这个分析其实有问题。
其余情况都有解。
下面给出标程:
#include<bits/stdc++.h>
#define N 600005
#define UI unsigned int
using namespace std;
char s[N];
UI ha[N],rha[N],pw[N];
const UI base=233;
const UI rv=534566745;
#define isp(l,r) ( ha[r]-ha[l-1]*pw[(r)-(l)+1]==rha[l]-rha[r+1]*pw[(r)-(l)+1])
int work(){
int n=strlen(s+1);
ha[0]=rha[n+1]=0;
for(int i=1;i<=n;i++) ha[i]=ha[i-1]*base+s[i];
for(int i=n;i>=1;i--) rha[i]=rha[i+1]*base+s[i];
int ansmx=0,ansmn=0,last=0;
bool cut=false;
for(int i=1;i<=n;i++)
if(isp(1,i)==0&&isp(i+1,n)==0){
cut=true;break;
}
if(isp(1,n)==0) ansmn=1;
else if(cut) ansmn=2;
else return puts("-1 -1"),0;
for(int i=1;i<=n;i++)
if(last==0) last++;
else if(s[i]!=s[i-1]) last=0,ansmx++;
if(n>=3&&n%2==1){
bool fl=true;
for(int i=1;i<=n;i+=2) if(s[i]!=s[1]) fl=false;
for(int i=2;i<=n;i+=2) if(s[i]==s[1]) fl=false;
if(fl) ansmx--;
}
if(!cut) ansmx=1;
printf("%d %d\n",ansmn,ansmx);
return ansmx;
}
int main(){
freopen("bhw.in","r",stdin);
freopen("bhw.out","w",stdout);
int T;scanf("%d",&T);
pw[0]=1;for(int i=1;i<N;i++) pw[i]=pw[i-1]*base;
while(T--){
scanf("%s",s+1);
work();
}
return 0;
}