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+i−11+i1+i+11+...+i−21+......+11+21+...+n−i+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+...+n−11+21+31+...+n1.
大胆猜想,
i
i
i比起
i
−
1
i-1
i−1的贡献来多了
1
i
+
1
i
+
1
+
.
.
.
+
1
n
−
i
+
1
\frac{1}{i}+\frac{1}{i+1}+...+\frac{1}{n-i+1}
i1+i+11+...+n−i+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[i−1]+v[n−i+1]−v[i−1].(前缀和)
最后
∑
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[i−1]=s[i+1],那么
d
p
[
i
−
1
]
[
i
+
1
]
dp[i-1][i+1]
dp[i−1][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阶回文串的左右两半部分(如果中间是一个字母,该字母不算)都是k−1阶回文串.求一个字符串中1→n阶回文串分别有多少个.
应该是和245H一样的水题.不过要注意到它还是比较有意思的.
首先用中心扩散预处理回文串.
接下来用区间dp的思想枚举区间长度,如果这个区间左右两半的回文串等级都为
i
i
i且不等于
0
0
0,这个区间的等级就是
i
+
1
i+1
i+1.
最后发现回文串的等级有包含性,
k
k
k阶回文串一定是
k
−
1
k-1
k−1阶回文串.
那么只要求后缀和就可以了.
#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;
}
谢谢大家.