第一题 不重最长子串
Description
给定一个字符串 s,请你找出其中不含有重复字符的最长子串的长度。
Input
一行,一个字符串 s,长度在 0∼50000 之间,由英文字母、数字和空格组成。
Output
输出一个整数,为不含有重复字符的最长子串的长度。
Samples
输入数据 1
abcabcbb
输出数据 1
3
Hint1
因为无重复字符的最长子串是 "abc",所以其长度为 3。
输入数据 2
bbbbb
输出数据 2
1
Hint2
因为无重复字符的最长子串是 "b",所以其长度为 1。
输入数据 3
pwwkew
输出数据 3
3
Hint3
因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
Limitation
1s, 1024KiB for each test case.
思路如下:
代码(一开始没有考虑带空格的输入直接用的cin输入导致拿了PAC 30分 用getline就AC了):
#include<bits/stdc++.h>
using namespace std;
string s;
set<char> sets;
int len,r=-1,l,sum;
int main()
{
getline(cin,s);
len=s.length();
while(l<len){
if (l != 0) {
sets.erase(s[l-1]);
}
while (r<len-1 && !sets.count(s[r+1])){
sets.insert(s[r+1]);
r++;
}
sum=max(sum,r-l+1);
l++;
}
cout<<sum;
return 0;
}
其他思路:
使用loc数组,记录每一个字符出现的地址,代码如下:
#include<iostream>
#include<cstring>
using namespace std;
const int N=307;
int loc[N],l,mx;
string s;
int main(){
getline(cin,s);
int len=s.size();
memset(loc,-1,sizeof(loc));
for(int i=0;i<len;i++){
if(loc[s[i]]>=l){
l=loc[s[i]]+1;
}else{
mx=max(mx,i-l+1)
}
loc[s[i]]=i;
}
cout<<mx;
return 0;
}
第二题 小J的加密算法
Description
上大学了,小J学会了一种加密算法,这种算法可以根据一个行数 mm,将一个字符串进行重新排列,变成无法读懂的密文。
例如:给定一个字符串 "PAYPALISHIRING",行数 3,将其进行从上到下,从左到右的Z字形排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
Format
Input
一个由英文字母及英文标点符号组成的字符串 ss 及一个行数 m(1≤m≤1000)m(1≤m≤1000),ss 的长度在 1∼10001∼1000 之间。
Output
输出加密字符串
Samples
输入数据 1
PAYPALISHIRING 3
输出数据 1
PAHNAPLSIIGYIR
输入数据 2
PAYPALISHIRING 4
输出数据 2
PINALSIGYAHRPI
Hint2
P I N
A L S I G
Y A H R
P I
输入数据 3
A 1
输出数据 3
A
输入数据 4
hello,world! 3
输出数据 4
horel,ol!lwd
Limitation
1s, 1024KiB for each test case.
思路如下:
代码(AC):
#include<iostream>
#include<string.h>
using namespace std;
string s[1007],c;
int m,n;
int main(){
cin>>c>>m;
if(m==1){
cout<<c;
return 0;
}
n=m*2-2;
for(int i=0;i<c.length();i++){
int row=i%n;
if(row<m){
s[row]+=c[i];
}else{
s[n-row]+=c[i];
}
}
for(int i=0;i<m;i++){
cout<<s[i];
}
return 0;
}
注意:规律不唯一,这个题目用很多别的思路或者规律都可以做出来
第三题:数列的个数
Description
给出两个整数 n,mn,m。问有多少个长度为 nn 的序列 AA,满足以下条件:
-
1≤Ai≤m(i=1,2,⋅⋅⋅,n)1≤Ai≤m(i=1,2,⋅⋅⋅,n)
-
∀i∈[1,n−1]∀i∈[1,n−1], Ai+1Ai+1 是 AiAi 的倍数。
由于答案可能很大,所以你只需要输出答案对 998244353998244353 取模的结果。
Format
Input
输入只有一行,两个整数 n,m(1≤n,m≤2×105)n,m(1≤n,m≤2×105)。
Output
输出只有一行,输出方案数。
Samples
输入数据 1
3 4
输出数据 1
13
输入数据 2
20 30
输出数据 2
71166
输入数据 3
200000 200000
输出数据 3
835917264
Limitation
1s, 1024KiB for each test case.
思路:
这题一开始我是什么也没想到,是后面等老师讲解了才有了思路。
由于这道题的规模很大,所以不可以暴力枚举。
通过观察,我们发现一个位置 pos 前面所有数字都是它的因子(或等于它自身),我们依次求解以 k 为结尾的,满足 Ai+1 是 Ai 倍数的 数列个数,设此子问题的解为 solve(k),那么最终答案就是 slove(1) + ..+solve(m)。
而对于以 k 为结尾的,满足 Ai+1 是 Ai 倍数的数列个数,则相当于将 k 的素因子分配到 n - 1 个位置上的组合数。
假设是n = 3,k = 4,而 4 的素因子是两个 2,所以前面两个位置的数字就相当于从这两个 2 中选取若干个(0,1,2个)进行分配。又由于前面 n-1 个位置上元素的先后顺序并不影响答案,即 1 2 4 和 2 1 4 不用可以区分,只区分每个元素出现多少次即可。假设 k 的某个素因子出现了 c 次,那么它对答案的贡献等于将 0~c 个球放入 n-1 个盒子中的方案数,所以问题的重点又变成了求排列组合问题。
假设 dp[i, j] 表示将 j 个球放入到 i 个盒子的方案数,那么 dp[i,j] = dp[i-1,j] + dp[i,j-1]
又由于每次只用到 i-1 和 i 位置,所以可以用空间压缩。
代码:
#include<bits/stdc++.h>
const int M = 998244353;
int dp[20] = {1}, n, m;
//dp[i,j] 表示前 i 个数字消耗掉 j 个因子的方案数,相当于完全背包,降维
int solve(int k) {
int ans=1;
for (int i=2;i*i<=k;++i){
if(k%i){
continue;
}
int c=0;
while(k%i==0){
k/=i;
c++;
}
ans = ans * 1LL * dp[c] % M; //记得每次都取模
}
if(k>1){
ans = ans * 1LL * dp[1] % M; //记得每次都取模
}
return ans;
}
int main() {
int ans = 0;
cin>>n>>m;
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= 19; ++j){
(dp[j] += dp[j-1]) %= M; //记得每次都取模
}
}
for (int i = 1; i <= m; ++i){
(ans += solve(i)) %= M;
}
cout<<ans;
return 0;
}
第四题:匹配正则表达式
Description
给你一个字符串 ss 和一个字符规律 pp,请你来实现一个支持 .
和 *
的正则表达式匹配。
-
.
匹配任意单个字符 -
*
匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 ss 的,而不是部分字符串。
Format
Input
多组测试数据 每组数据一行,两个字符串 ss(长度小于20) 和 pp(长度小于30),ss 只包含小写字母,pp 包含小写字母以及 .
和 *
。
Output
每组数据输出一行,如果 pp 能够匹配 ss,则输出 Yes
,否则输出 No
。
Samples
输入数据 1
aa a
aa aa
aa a.
aa b*aa
aa c*a.
输出数据 1
No
Yes
Yes
Yes
Yes
Limitation
1s, 1024KiB for each test case.
思路:
目前思考到的是动态规划 想到的是可以用二维dp,使dp[i][j]表示s[i]和p[j]的字符是否匹配,但没有想好动态转移方程。