Codeforces 字符串五题题解 509E,245H,514C,724D,835D

55 篇文章 0 订阅
26 篇文章 0 订阅

文章目录

509E

求 一 个 字 符 串 每 一 个 子 串 中 元 音 字 符 占 比 的 和 . 求一个字符串每一个子串中元音字符占比的和. .
稍微推式子,可以发现如果在长度为 n n n的字符串中的第 i i i个字符是元音字符,它所产生的贡献是:
1 i + 1 i + 1 + 1 i + 2 + . . . + 1 n + 1 i − 1 + 1 i + 1 i + 1 + . . . + 1 i − 2 + . . . . . . + 1 1 + 1 2 + . . . + 1 n − i + 1 \frac{1}{i}+\frac{1}{i+1}+\frac{1}{i+2}+...+\frac{1}{n}+\frac{1}{i-1}+\frac{1}{i}+\frac{1}{i+1}+...+\frac{1}{i-2}+......+\frac{1}{1}+\frac{1}{2}+...+\frac{1}{n-i+1} i1+i+11+i+21+...+n1+i11+i1+i+11+...+i21+......+11+21+...+ni+11
如果这个式子表达不够明显,我们来看当 i = 1 i=1 i=1的时候,贡献等于 1 1 + 1 2 + . . . + 1 n \frac{1}{1}+\frac{1}{2}+...+\frac{1}{n} 11+21+...+n1.
i = 2 i=2 i=2的时候贡献是 1 1 + 1 2 + . . . + 1 n − 1 + 1 2 + 1 3 + . . . + 1 n \frac{1}{1}+\frac{1}{2}+...+\frac{1}{n-1}+\frac{1}{2}+\frac{1}{3}+...+\frac{1}{n} 11+21+...+n11+21+31+...+n1.
大胆猜想, i i i比起 i − 1 i-1 i1的贡献来多了 1 i + 1 i + 1 + . . . + 1 n − i + 1 \frac{1}{i}+\frac{1}{i+1}+...+\frac{1}{n-i+1} i1+i+11+...+ni+11.
这样我们预处理 v [ i ] = 1 1 + 1 2 + . . . + 1 i v[i]=\frac{1}{1}+\frac{1}{2}+...+\frac{1}{i} v[i]=11+21+...+i1.
再处理 s u m [ i ] = s u m [ i − 1 ] + v [ n − i + 1 ] − v [ i − 1 ] . ( 前 缀 和 ) sum[i]=sum[i-1]+v[n-i+1]-v[i-1].(前缀和) sum[i]=sum[i1]+v[ni+1]v[i1].()
最后 ∑ i = 1 n s u m [ i ] ( s i ∈ { ′ A ′ , ′ E ′ , ′ I ′ , ′ O ′ , ′ U ′ , ′ Y ′ } ) \sum_{i=1}^{n}sum[i](s_i\in\{'A','E','I','O','U','Y'\}) i=1nsum[i](si{A,E,I,O,U,Y})即为答案.
说得很清楚了,就不给代码了.

245H

多 组 询 问 一 个 字 符 串 [ l , r ] 区 间 中 有 多 少 个 回 文 串 . 多组询问一个字符串[l,r]区间中有多少个回文串. [l,r].
这题是个好题,当然做法也不止区间 d p dp dp一种.
我们考虑 O ( n 2 ) O(n^2) O(n2)预处理, O ( 1 ) O(1) O(1)询问.
首先用中点向外扩散法预处理哪些区间是回文串,这个的复杂度是 O ( n 2 ) O(n^2) O(n2).
如果你不知道怎么中点向外扩散,我来说明一下.
假设 d p [ l ] [ r ] dp[l][r] dp[l][r]表示 [ l , r ] [l,r] [l,r]区间的子串是否是回文串.
显然 d p [ i ] [ i ] dp[i][i] dp[i][i]肯定为 1 1 1.
如果 s [ i − 1 ] = s [ i + 1 ] s[i-1]=s[i+1] s[i1]=s[i+1],那么 d p [ i − 1 ] [ i + 1 ] dp[i-1][i+1] dp[i1][i+1]也是 1 1 1.
这样子每次设两个指针 j , k j,k j,k,一个往左,一个往右,直到到头或者 s [ j ] s[j] s[j] s [ k ] s[k] s[k]不相同break.
注意回文串有奇数长度和偶数长度两种情况, j , k j,k j,k的初值有所不同,都要处理.
接下来把 d p dp dp数组输出来观察,并感性理解.
对于 [ l , r ] [l,r] [l,r]的所有子串,它的左右端点都在 [ l , r ] [l,r] [l,r]之间.
如果反映在 d p dp dp数组上,就是 ( l , l ) (l,l) (l,l) ( r , r ) (r,r) (r,r)这个子矩阵了.
那么这个区间内回文串的个数就是 d p dp dp数组这个子矩阵的和.
这样只要对 d p dp dp数组求一个二维前缀和就可以过了.

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int aoi=5038;
char s[aoi]; 
int dp[aoi][aoi];
int main() {
scanf("%s",s+1);
int i,j,k,q=read(),n=strlen(s+1);
for (i=1;i<=n;++i) {
  for (j=k=i;j&&k<=n&&s[j]==s[k];--j,++k) ++dp[j][k];
  for (j=i,k=i+1;j&&k<=n&&s[j]==s[k];--j,++k) ++dp[j][k];
  }
for (i=1;i<=n;++i)
  for (j=1;j<=n;++j)
    dp[i][j]+=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1];
for (int l,r;q--;) {
  read(l),read(r);
  write(dp[r][r]-dp[l-1][r]-dp[r][l-1]+dp[l-1][l-1]),pl;
  }
}

514C

每 次 询 问 是 否 存 在 某 个 字 符 串 和 询 问 字 符 串 恰 好 有 一 个 字 符 不 同 . 每次询问是否存在某个字符串和询问字符串恰好有一个字符不同. .
考虑字符串哈希.
由于所有的字符只有 a , b , c a,b,c a,b,c,只要直接把询问的字符串暴力改变一个字符,然后看它的哈希值有没有出现即可.
只要会字符串哈希,这题就很水了.
注意小于 m a x i n t maxint maxint的模数会被卡掉,要开非常大的模数或者双模哈希.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rel register ll
#define rec register char
#define gc getchar
//#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?-1:*p1++)
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
char buf[1<<23],*p1=buf,*p2=buf;
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) pc('-'),x=-x;
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=3e5,kp=23;
const ll mod=233333333377451ll; // 应该随便打一个比较大的模数就可以过了.
typedef int fuko[yuzu|10];
typedef ll rize[yuzu|10];
set<ll> s;
rize bs;
ll ghash(string &p) {
ll zxy=0;
for (int i=0;i<p.size();++i) zxy=(zxy+1ll*p[i]*bs[i])%mod;
return zxy;
}
int main() {
int i,n,m,j;
for (*bs=i=1;i<=yuzu;++i) bs[i]=bs[i-1]*kp%mod;
read(n),read(m);
string lxy;
for (i=1;i<=n;++i) cin>>lxy,s.insert(ghash(lxy));
for (;m--;) {
  cin>>lxy;
  ll llx=0,zxy=ghash(lxy);
  for (i=0;i<lxy.size();++i)
    for (j='a';j<='c';++j) if (j^lxy[i])
      llx|=s.count((zxy-(lxy[i]-j)*bs[i]+(mod*1ll<<2))%mod);
  puts(llx?"YES":"NO");
  }   
}

724D

在 字 符 串 中 选 择 一 些 字 母 , 但 保 证 每 一 个 长 度 为 m 的 区 间 都 至 少 选 了 一 个 字 母 . 求 选 出 字 母 组 成 字 典 序 最 小 的 字 符 串 . 在字符串中选择一些字母,但保证每一个长度为m的区间都至少选了一个字母.\newline求选出字母组成字典序最小的字符串. ,m..
考虑贪心.
我们对于每一个长度为 m m m的区间贪心找到最小并且最后面的字符.
这样子能够保证每一个长度为 m m m的区间都至少选择了一个字符.
但是还没完.我们刚才贪心出的所有字符有一个最大值 m x mx mx.如果不把原字符串里所有小于 m x mx mx的字符全部选上,答案仍然不是字典序最小的.
选出所有小于 m x mx mx的字符之后,答案就是最小的了,可以感性理解.

#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rel register ll
#define rec register char
#define gc getchar
//#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?-1:*p1++)
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
char buf[1<<23],*p1=buf,*p2=buf;
inline int read(){
  int x=0,f=1;char c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return f?x:-x;
  }
template <typename mitsuha>
inline bool read(mitsuha &x){
  x=0;int f=1;char c=gc();
  for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
  if (!~c) return 0;
  for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
  return x=f?x:-x,1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return 0&pc(48);
  if (x<0) pc('-'),x=-x;
  int bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  return 0;
  }
inline char fuhao(){
  char c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
const int yuzu=1e5;
char c[yuzu|10],da,obuf[yuzu|10],*ob=obuf; // obuf是类似于快输的写法.
int vis[yuzu|10];
int main() {
int m=read(),i,j,k,n;
scanf("%s",c+1),n=strlen(c+1);
for (i=1;i<=n-m+1;*ob++=c[k],da=max(da,c[k]),vis[k]=1,i=++k) // i可以直接赋值为k+1,因为前面的区间里显然已经有字符选出来了.
  for (j=k=i;j<i+m;++j) if (c[j]<=c[k]) k=j; // 找到最小字符位置k.
for (i=1;i<=n;++i) if (c[i]<da&&!vis[i]) *ob++=c[i]; // 小于最大值没有选过的所有字符都选上.
sort(obuf,ob),puts(obuf); // 最后排个序输出就可以了.
}

835D

定 义 普 通 回 文 串 是 1 阶 回 文 串 , k 阶 回 文 串 的 左 右 两 半 部 分 ( 如 果 中 间 是 一 个 字 母 , 该 字 母 不 算 ) 都 是 k − 1 阶 回 文 串 . 求 一 个 字 符 串 中 1 → n 阶 回 文 串 分 别 有 多 少 个 . 定义普通回文串是1阶回文串,k阶回文串的左右两半部分\newline (如果中间是一个字母,该字母不算)都是k-1阶回文串.\newline求一个字符串中1\to n阶回文串分别有多少个. 1,k(,)k1.1n.
应该是和245H一样的水题.不过要注意到它还是比较有意思的.
首先用中心扩散预处理回文串.
接下来用区间dp的思想枚举区间长度,如果这个区间左右两半的回文串等级都为 i i i且不等于 0 0 0,这个区间的等级就是 i + 1 i+1 i+1.
最后发现回文串的等级有包含性, k k k阶回文串一定是 k − 1 k-1 k1阶回文串.
那么只要求后缀和就可以了.

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int aoi=5038;
char s[aoi];
int dp[aoi][aoi],sum[aoi];
int main(){
scanf("%s",s+1);
int i,n=strlen(s+1),j,k;
for (i=1;i<=n;++i) {
  for (j=k=i;j&&k<=n&&s[j]==s[k];--j,++k) dp[j][k]++;
  for (j=i,k=i+1;j&&k<=n&&s[j]==s[k];--j,++k) dp[j][k]++;
  }
for (k=2;k<=n;++k) 
  for (i=1;i+k-1<=n;++i)
    if (dp[i][i+k-1]&&dp[i][i+k/2-1]&&dp[i][i+k/2-1]==dp[i+k-k/2][i+k-1])
      dp[i][i+k-1]+=dp[i][i+k/2-1];
for (i=1;i<=n;++i)
  for (j=1;j<=n;++j)
    if (dp[i][j]) ++sum[dp[i][j]];
for (i=n;i;--i) sum[i]+=sum[i+1];
for (i=1;i<=n;++i) write(sum[i]),p32;
}

谢谢大家.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值