2022CSP-J集训测试1 笔记

 第一题 不重最长子串

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]的字符是否匹配,但没有想好动态转移方程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值