也许更好的阅读体验

\(\mathcal{Description}\)

定义一种字符串\(gray\)串满足:

  • 长度为奇数
  • 正中间的字母只出现一次
  • 左右两端相同,左右两端也是gray串

一个\(gray\)串的贡献为这个串长度的平方

需要注意的是一个长度为\(7\)的\(gray\)串是包含了长度为\(1,3\)的\(gray\)的

现给你一个长为\(n(n<=100,000)\)的字符串,你可以修改至多一个字母,使得总贡献值最大

输入方式为一个字符串

\(\mathcal{Solution}\)

  • 考虑怎么去求修改字符的贡献

    我们修改一个字符时应该考虑两点:

    一是该字符原本造成的贡献没有了

    二是将其修改后会得到新的贡献

    当我们对第\(i\)个字符修改时

    用\(cost[i]\)表示\(i\)位置原本的贡献

    用\(bef[i][x](benefit)\)表示将\(i\)位置的字符修改为\(x\)所得到的贡献,特别的,设i位置本来的字符为\(x\),那么\(cost[i]=bef[i][x]\)

    那么将\(i\)位置的字符修改为\(x\)得到的贡献应为\(bef[i][x]-cost[i]\)

  • 根据\(gray\)串的定义,我们知道一个\(gray\)串的长度应该是\(1,3,7,15,31\dots(2^x-1)\)

    所以我们最多有\(log_2(n)\)种长度不同的\(gray\)串,\(n\)最大为\(100,000\)也就是说最多有\(16\)种

    所以我们可以考虑求出所有长度的情况

  • 求\(cost\)

    \(cost[i]\)表示的是\(i\)位置的字符原本的贡献,其包含了不同长度的贡献

    一个长度为\(n\)的\(gray\)串所有位置的贡献应该都是\(n*n\)

    考虑差分,找出所有不同长度的\(gray\)串,在其开头贡献加上\(n*n\),在其末尾加一的位置减去\(n*n\)

  • 如何判断原串中一个串是否为\(gray\)串(过水可跳过)

    先将原串哈希一遍

    用\(pef[i][j]\)表示以\(i\)为开头,长度为\(2^j-1\)的串是否为\(gray\)串 \((\)我喜欢叫它完美串\(pef \Rightarrow perfect)\)

    用\(sum[i][j]\)表示字符\(i\)在前\(j\)个字符中出现了多少次

    设一个长度为\(2^i-1\)的串左端点为\(l\),中点为\(mid\),右端点为\(r\),中点字符为\(ch\),字符串的哈希值为\(sub\)

    则应满足以下条件

    \(sum[ch][r]-sum[ch][l-1]==1\)

    \(pef[l][i-1]=true\)

    \(pef[mid+1][i-1]=true\)

    \(sub(l,mid-1)==sub(mid+1,r)\)

  • 求\(bef\)

    枚举所有长度以及其起点

    考虑将其变为\(gray\)串

    此时对于一个串有两种情况

    一是左半边与右半边都是\(gray\)串,但是中间的字符有重复,枚举将中间的字符修改为什么字符即可

    二是左半边与右半边有一个为\(gray\)串,此时两串不相同的字符应只有一个并且中间的字符应不会重复

    这一段的打法并没有特殊技巧,注意处理方法即可,细节看代码,思路很清晰

\(\mathcal{Code}\)

代码里有折叠,使用vim的朋友可以将其分开看

建议打代码时也分开打

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年07月19日 星期五 20时41分51秒
*******************************/
#include <iostream>
#include <fstream>
#include <cstring>
#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;
}

如有哪里讲得不是很明白或是有错误,欢迎指正

如您喜欢的话不妨点个赞收藏一下吧

如能得到推荐博主就更开心了

您的鼓励是博主的动力

CF356E - Xenia and String Problem的更多相关文章

  1. Codeforces 356E - Xenia and String Problem(哈希)

    Codeforces 题面传送门 & 洛谷题面传送门 首先显然一个 gray 串的长度只可能是 \(2^k-1\),其中 \(k\in\mathbb{Z}\). 考虑将一个字符改成另外一个字符 ...

  2. hdu String Problem(最小表示法入门题)

    hdu 3374 String Problem 最小表示法 view code#include <iostream> #include <cstdio> #include &l ...

  3. HDU 3374 String Problem(KMP+最大/最小表示)

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  4. 【HDU3374】 String Problem (最小最大表示法+KMP)

    String Problem Description Give you a string with length N, you can generate N strings by left shift ...

  5. HDOJ3374 String Problem 【KMP】+【最小表示法】

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  6. HDU 3374 String Problem (KMP+最大最小表示)

    HDU 3374 String Problem (KMP+最大最小表示) String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory ...

  7. String Problem hdu 3374 最小表示法加KMP的next数组

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  8. ACM-ICPC2018南京赛区 Mediocre String Problem

    Mediocre String Problem 题解: 很容易想到将第一个串反过来,然后对于s串的每个位置可以求出t的前缀和它匹配了多少个(EXKMP 或者 二分+hash). 然后剩下的就是要处理以 ...

  9. hdu3374 String Problem【最小表示法】【exKMP】

    String Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

随机推荐

  1. Android实现dialog时候弹出软键盘dialog移位问题

    Window win = getWindow(); WindowManager.LayoutParams params = win.getAttributes(); win.setSoftInputM ...

  2. Delphi6/7 中XML 文档的应用

    XML文档是新一代的Web数据格式.它可以用树的形式储存一切数据.下面介绍一下TXMLDocument控件的一些用法:已定义:XMLDoc: TXMLDocument;EncoderMIME: TId ...

  3. Cocos2d-x 3.X Qt MinGW版本编译运行

    自Cocos2d-x 3.X引入了C++ 11特性,在Windows平台上的支持就仅限VS 2012,其实还可以尝试MinGW版本,GitHub上有MinGW版本的Qt Creator工程. 地址:h ...

  4. C#每天进步一点--引用类型和值类型

    在刚参加工作面试时,我们经常会遇到有关值类型和引用类型的问题,你回答的怎么样直接影响你在别人心目中的印象,你回答的不好说明你对C#没有深入的了解学习,今天我带大家回顾下C#中的引用类型和值类型. CL ...

  5. Ruby元编程:单元测试框架如何找到测试用例

    前几天看了Google Testing Blog上的一篇文章讲到C++因为没有反射机制,所以如何注册测试用例就成了一件需要各显神通的事情.从我的经验来看,无论是Google的GTest还是微软的LTM ...

  6. C C++ Java C# JS编译、执行过程的原理入门分析

    C.C++是典型的编译型编程语言,编译链接后,点击则可执行. JS,解释型脚本语言,则不需要进行编译,直接解释执行. Java和C#则是所谓的高级语言,编译执行的方式做了很多处理, 尤其是C#,VS编 ...

  7. spring之@Value详解(转载)

    @Value注入 不通过配置文件的注入属性的情况 通过@Value将外部的值动态注入到Bean中,使用的情况有: 注入普通字符串 注入操作系统属性 注入表达式结果 注入其他Bean属性:注入beanI ...

  8. 使用LinkedList模拟一个堆栈或者队列数据结构。

    堆栈:先进后出 First in last out filo 队列:先进先出 First in last out filo使用LinkedList的方法,addFirst addLast getFir ...

  9. 读书笔记——《谁说菜鸟不会数据分析—Python篇》

    最近刚读完一本新书,关注的公众号作者出的“谁说菜鸟不会数据分析—Python篇”,话说现在很多微信公众号大牛都在出书,这貌似是一个趋势.. 说说这本书吧,我之前看过一些网文,对于数据分析这一块也有过一 ...

  10. Spring 之Aop实现日志记录

    Aop实现见代码,简单demo实现 package com.idcos.automate.config; import com.idcos.automate.dal.auto.dao.dcos.Dco ...