也许更好的阅读体验
D e s c r i p t i o n \mathcal{Description} Description
定义一种字符串 g r a y gray gray串满足:
一个 g r a y gray gray串的贡献为这个串长度的平方
需要注意的是一个长度为 7 7 7的 g r a y gray gray串是包含了长度为 1 , 3 1,3 1,3的 g r a y gray gray的
现给你一个长为 n ( n < = 100 , 000 ) n(n<=100,000) n(n<=100,000)的字符串,你可以修改至多一个字母,使得总贡献值最大
输入方式为一个字符串
S o l u t i o n \mathcal{Solution} Solution
考虑怎么去求修改字符的贡献
我们修改一个字符时应该考虑两点:
一是该字符原本造成的贡献没有了
二是将其修改后会得到新的贡献
当我们对第 i i i个字符修改时
用 c o s t [ i ] cost[i] cost[i]表示 i i i位置原本的贡献
用 b e f [ i ] [ x ] ( b e n e f i t ) bef[i][x](benefit) bef[i][x](benefit)表示将 i i i位置的字符修改为 x x x所得到的贡献,特别的,设i位置本来的字符为 x x x,那么 c o s t [ i ] = b e f [ i ] [ x ] cost[i]=bef[i][x] cost[i]=bef[i][x]
那么将 i i i位置的字符修改为 x x x得到的贡献应为 b e f [ i ] [ x ] − c o s t [ i ] bef[i][x]-cost[i] bef[i][x]−cost[i]
根据 g r a y gray gray串的定义,我们知道一个 g r a y gray gray串的长度应该是 1 , 3 , 7 , 15 , 31 … ( 2 x − 1 ) 1,3,7,15,31\dots(2^x-1) 1,3,7,15,31…(2x−1)
所以我们最多有 l o g 2 ( n ) log_2(n) log2(n)种长度不同的 g r a y gray gray串, n n n最大为 100 , 000 100,000 100,000也就是说最多有 16 16 16种
所以我们可以考虑求出所有长度的情况
求 c o s t cost cost
c o s t [ i ] cost[i] cost[i]表示的是 i i i位置的字符原本的贡献,其包含了不同长度的贡献
一个长度为 n n n的 g r a y gray gray串所有位置的贡献应该都是 n ∗ n n*n n∗n
考虑差分,找出所有不同长度的 g r a y gray gray串,在其开头贡献加上 n ∗ n n*n n∗n,在其末尾加一的位置减去 n ∗ n n*n n∗n
如何判断原串中一个串是否为 g r a y gray gray串(过水可跳过)
先将原串哈希一遍
用 p e f [ i ] [ j ] pef[i][j] pef[i][j]表示以 i i i为开头,长度为 2 j − 1 2^j-1 2j−1的串是否为 g r a y gray gray串 ( ( (我喜欢叫它完美串 p e f ⇒ p e r f e c t ) pef \Rightarrow perfect) pef⇒perfect)
用 s u m [ i ] [ j ] sum[i][j] sum[i][j]表示字符 i i i在前 j j j个字符中出现了多少次
设一个长度为 2 i − 1 2^i-1 2i−1的串左端点为 l l l,中点为 m i d mid mid,右端点为 r r r,中点字符为 c h ch ch,字符串的哈希值为 s u b sub sub
则应满足以下条件
s u m [ c h ] [ r ] − s u m [ c h ] [ l − 1 ] = = 1 sum[ch][r]-sum[ch][l-1]==1 sum[ch][r]−sum[ch][l−1]==1
p e f [ l ] [ i − 1 ] = t r u e pef[l][i-1]=true pef[l][i−1]=true
p e f [ m i d + 1 ] [ i − 1 ] = t r u e pef[mid+1][i-1]=true pef[mid+1][i−1]=true
s u b ( l , m i d − 1 ) = = s u b ( m i d + 1 , r ) sub(l,mid-1)==sub(mid+1,r) sub(l,mid−1)==sub(mid+1,r)
求 b e f bef bef
枚举所有长度以及其起点
考虑将其变为 g r a y gray gray串
此时对于一个串有两种情况
一是左半边与右半边都是 g r a y gray gray串,但是中间的字符有重复,枚举将中间的字符修改为什么字符即可
二是左半边与右半边有一个为 g r a y gray gray串,此时两串不相同的字符应只有一个并且中间的字符应不会重复
这一段的打法并没有特殊技巧,注意处理方法即可,细节看代码,思路很清晰
C o d e \mathcal{Code} Code
代码里有折叠,使用vim的朋友可以将其分开看
建议打代码时也分开打
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年07月19日 星期五 20时41分51秒
*******************************/
#include
#include
#include
#define ll long long
#define ull unsigned long long
using namespace std;
const int maxn = 100050;
const int limit = 16;
const int h = 31;
int n;
int len[maxn];
ll ans,inc;
ll cost[maxn];
ll bef[maxn][30];
ull ha[maxn],mi[maxn];
int sum[30][maxn];
char s[maxn];
bool pef[maxn][limit+5];
//{{{sub
inline ull sub (int l,int r)
{
return ha[r]-ha[l-1]*mi[r-l+1];
}
//}}}
//{{{check
inline bool check (int l1,int r1,int l2,int r2)
{
return sub(l1,r1)==sub(l2,r2);
}
//}}}
//{{{lcp
inline int lcp (int l1,int l2)
{
int res=0;
for (int i=limit;i>=0;--i){
int le=res+len[i];
if (l1+le<=n&&l2+le<=n&&check(l1,l1+le,l2,l2+le)) res+=len[i]+1;
}
return res;
}
//}}}
//{{{init
inline void init ()
{
for (int i=1;i<=limit+1;++i) len[i]=len[i-1]<<1|1;
mi[0]=1,ans=n=strlen(s+1);
for (int i=1;i<=n;++i){
for (int j=1;j<=26;++j)
sum[j][i]=sum[j][i-1]+(s[i]-'a'+1==j);
pef[i][1]=true,mi[i]=mi[i-1]*h;
ha[i]=ha[i-1]*h+s[i]-'a'+1;
}
}
//}}}
//{{{get_pef
inline void get_pef()
{
for (int i=2;len[i]<=n;++i)
for (int l=1,r=len[i];r<=n;++l,++r){
int mid=l+len[i-1],ch=s[mid]-'a'+1;
if (sum[ch][r]-sum[ch][l-1]==1) pef[l][i]=pef[l][i-1]&pef[mid+1][i-1]&check(l,mid-1,mid+1,r);
ans+=1ll*pef[l][i]*len[i]*len[i];
}
}
//}}}
//{{{get_cost
inline void get_cost ()
{
for (int i=1;len[i]<=n;++i)
for (int l=1,r=len[i];r<=n;++l,++r){
cost[l]+=1ll*pef[l][i]*len[i]*len[i];
cost[r+1]-=1ll*pef[l][i]*len[i]*len[i];
}
for (int i=1;i<=n;++i) cost[i]+=cost[i-1];
}
//}}}
//{{{calc
inline void calc (int l1,int k)// from l1 len[k]
{
if (k==1){
for (int i=1;i<=26;++i) ++bef[l1][i];
return;
}
int mid=l1+len[k-1],l2=mid+1;
ll tmp=1ll*len[k]*len[k];
if (pef[l1][k-1]&&pef[l2][k-1]&&check(l1,mid-1,l2,l2+len[k-1]-1)){
for (int i=1;i<=26;++i)
if (sum[i][l1-1]==sum[i][mid-1]) bef[mid][i]+=tmp;
return;
}
int prf1=lcp(l1,l2);
int m1=l1+prf1,m2=l2+prf1;
int prf2=lcp(m1+1,m2+1);
if (m1+1+prf2<mid) return;
if (pef[l1][k-1]&&sum[s[mid]-'a'+1][l1-1]==sum[s[mid]-'a'+1][mid-1]) bef[m2][s[m1]-'a'+1]+=tmp;
if (pef[l2][k-1]&&sum[s[mid]-'a'+1][mid]==sum[s[mid]-'a'+1][l2+len[k-1]-1]) bef[m1][s[m2]-'a'+1]+=tmp;
}
//}}}
int main()
{
freopen("356E.in","r",stdin);
freopen("356E.out","w",stdout);
ios::sync_with_stdio(false);
cin>>(s+1);
init();
get_pef();
get_cost();
for (int i=1;i<=n;++i)
for (int j=1;i+len[j]-1<=n;++j)
calc(i,j);
for (int i=1;i<=n;++i)
for (int j=1;j<=26;++j)
inc=max(inc,bef[i][j]-cost[i]);
cout<<ans+inc<<endl;
return 0;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧
如能得到推荐博主就更开心了
您的鼓励是博主的动力