P8835 [传智杯 #3 决赛] 子串

记录57

#include<bits/stdc++.h>
using namespace std;
int main(){
	int T,n,m,cnt=0;
	string s1,s2,s3;
	cin>>T;
	while(T--){
		cin>>n>>m;
		cin>>s1>>s2;
		cnt=0;
		for(int i=0;i<n;i++) if(s1[i]>='A'&&s1[i]<='Z') s1[i]+='a'-'A';
		for(int i=0;i<m;i++) if(s2[i]>='A'&&s2[i]<='Z') s2[i]+='a'-'A';
		for(int i=0;i<=m-n;i++){//i+n-1<=m-1的变形
			s3=s2.substr(i,n);
			if(s1==s3){
				cnt++;			
			} 
		}
		cout<<cnt<<endl;
	}
	return 0;
} 

题目传送门https://www.luogu.com.cn/problem/P8835


突破点

给定 T 组询问,每次给定 2 个长度为 n,m 的只含英文字母的字符串 a,b,求 a 在 b 中的出现次数,相同字符不区分大小写。注意 a 是 b 中连续子序列。


思路

  1. 将字符串所有的大写转换成小写
  2. 将a字符串轮流比较b字符的每一段

代码简析

#include<bits/stdc++.h>
using namespace std;
int main(){
	int T,n,m,cnt=0;
	string s1,s2,s3;
	cin>>T;
	while(T--){
		cin>>n>>m;
		cin>>s1>>s2;
		cnt=0;
		for(int i=0;i<n;i++) if(s1[i]>='A'&&s1[i]<='Z') s1[i]+='a'-'A';
		for(int i=0;i<m;i++) if(s2[i]>='A'&&s2[i]<='Z') s2[i]+='a'-'A';
		...
	return 0;
} 

int T(数据组数),n(字符串a的长度),m(字符串b的长度),cnt=0(连续子序列出现次数);

string s1(字符串a),s2(字符串b),s3(字符串中的连续子序列);

两个for循环将所有大写字符转换为小写

#include<bits/stdc++.h>
using namespace std;
int main(){
	int T,n,m,cnt=0;
	string s1,s2,s3;
	cin>>T;
	while(T--){
		cin>>n>>m;
		cin>>s1>>s2;
		cnt=0;
		for(int i=0;i<n;i++) if(s1[i]>='A'&&s1[i]<='Z') s1[i]+='a'-'A';
		for(int i=0;i<m;i++) if(s2[i]>='A'&&s2[i]<='Z') s2[i]+='a'-'A';
		for(int i=0;i<=m-n;i++){//i+n-1<=m-1的变形
			s3=s2.substr(i,n);
			if(s1==s3){
				cnt++;			
			} 
		}
		cout<<cnt<<endl;
	}
	return 0;
} 

for(int i=0;i<=m-n;i++){}  👉  连续子序列的开始位置

解释:因为i是开始位置,长度为n,去掉开始的头i,长为n-1,a字符串尾巴最多到i+n-1,又因为b字符串尾巴位置最多到m-1(因为从0开始),所以i+n-1<=m-1,即i<=m-n。

s3=s2.substr(i,n);  👉  得到连续子序列

if(s1==s3){cnt++;}  👉  如果连续子序列跟字符串a相同,计数统计


补充

字符串处理函数完全汇总


一、C风格字符串函数(<cstring>

函数用途时间复杂度竞赛示例⚠️ 陷阱
**strlen() **求长度(到'\0')O(n)int len = strlen(s);无'\0'越界
** strcpy()**复制字符串O(n)strcpy(dest, src);dest必须够大
strcat()拼接字符串O(n)strcat(s, "abc");易溢出
strcmp()比较字典序O(n)if (strcmp(a, b) < 0)返回int,非bool
sprintf()格式化到char[]O(n)sprintf(buf, "%d", x);缓冲区溢出
sscanf()从char[]解析O(n)sscanf(buf, "%d", &x);解析失败无警告
strchr()找字符首次出现O(n)char* p = strchr(s, 'a');未找到返回NULL
strstr()找子串首次出现O(n)char* p = strstr(s, "abc");未找到返回NULL
strtok()分割字符串O(n)char* tok = strtok(s, ",");破坏原串

CSP-J推荐少用C函数,除非必须与scanf/printf混用!


二、C++ string类成员函数(<string>

函数用途时间复杂度竞赛示例⚠️ 陷阱
size()/length()求长度O(1)int len = s.size();
operator[]随机访问O(1)s[i] = 'a';越界不检查
at()安全访问O(1)s.at(i)越界抛异常(不用)
substr(pos, len)取子串O(len)s.substr(1, 3)返回新string
find(str)找子串位置O(n)size_t pos = s.find("abc");未找到返回string::npos
rfind(str)反向查找O(n)s.rfind("abc")同上
replace(pos, len, str)替换O(n)s.replace(0, 2, "xx")会改变原串
insert(pos, str)插入O(n)s.insert(0, "abc")少用,慢
erase(pos, len)删除O(n)s.erase(0, 2)少用,慢
append(str)尾部拼接O(n)s.append("abc")等价于+=
push_back(c)尾部加字符O(1)s.push_back('a')推荐
pop_back()尾部删字符O(1)s.pop_back()推荐
c_str()转C字符串O(1)printf("%s", s.c_str())必须转换
data()字符指针O(1)s.data()不保证\0
clear()清空O(1)s.clear()size变0
empty()判空O(1)if (s.empty())推荐

CSP-J推荐首选size()push_back()substr()find()


三、C++算法库函数(<algorithm><cctype>

函数用途时间复杂度竞赛示例⚠️ 陷阱
sort()排序O(n log n)sort(s.begin(), s.end())改变原串
reverse()翻转O(n)reverse(s.begin(), s.end())
isalpha(c)判字母O(1)if (isalpha(s[i]))需包含<cctype>
isdigit(c)判数字O(1)if (isdigit(s[i]))同上
islower(c)判小写O(1)if (islower(s[i]))同上
isupper(c)判大写O(1)if (isupper(s[i]))同上
tolower(c)转小写O(1)c = tolower(s[i])返回int,需转char
toupper(c)转大写O(1)c = toupper(s[i])同上
unique()去重相邻O(n)s.erase(unique(s.begin(), s.end()), s.end())只去相邻重复
next_permutation()下一个排列O(n)while(next_permutation(s.begin(), s.end()))需先排序

CSP-J推荐:** isalpha(), isdigit(), tolower() 高频使用 **


四、C++11/14/17新特性(CSP-J可用)

函数用途竞赛示例备注
** stoi(s)**string转intint x = stoi("123")错误抛异常
stol(s)string转longlong x = stol(s)-
stoll(s)string转long longll x = stoll(s)推荐
to_string(x)数字转stringstring s = to_string(123)推荐
ostringstream流式拼接ostringstream oss; oss << a << b;+

CSP-J推荐stoll() + to_string() 处理数字字符串转换


五、性能对比与竞赛建议

速度排行(快→慢)

O(1)操作:size(), push_back(), c_str()
↓
O(1) per char: operator[], at()
↓
O(n)简单:reverse(), isalpha()
↓
O(n)复杂:sort(), unique()
↓
O(n*m):频繁insert/erase(绝对避免)

CSP-J金牌模板

#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    // 1. 输入(string)
    string s;
    cin >> s;  // 自动处理空格
    
    // 2. 遍历(范围for)
    for (char c : s) {
        if (islower(c)) {  // 判断
            c = toupper(c); // 转换
        }
    }
    
    // 3. 查找(find)
    size_t pos = s.find("abc");
    if (pos != string::npos) {
        s.replace(pos, 3, "xyz");  // 替换
    }
    
    // 4. 排序(sort)
    sort(s.begin(), s.end());
    
    // 5. 输出
    cout << s << endl;  // 或printf("%s", s.c_str());
    
    return 0;
}

六、一句话总结

CSP-J中:string用size()+push_back()+find()+sort(),字符用isalpha()+tolower(),转数字用stoll(),转字符串用to_string(),C函数少用,混用printf记得c_str()

### 关于 P6464 #2 决赛 送门 树形动态规划 题解 #### 背景描述 题目涉及 n 座教学楼以及 m 条连接这些教学楼的双向道路,形成了一张无向图。目标是在这张图上执行特定的操作并求得最优解。 #### 解决方案概述 对于此类问题,通常采用树形动态规划 (Tree DP) 方法来解决。该方法适用于具有层次结构的数据集,在本案例中即为由教学楼构成的网络拓扑结构[^1]。 #### 动态规划状态定义 设 `f[u][i]` 表示以节点 u 作为根结点的子树内选择了 i 所能获得的最大价值,则可以建立如下转移方程: ```cpp for each child v of node u { for j from size[v] downto 0 { // 反向枚举防止重复计算 f[u][j + k] = max(f[u][j + k], f[u][j] + f[v][k]); } } ``` 这里假设已知所有孩子节点的状态,并通过上述方式更新父节点的状态。其中 `size[]` 数组记录各棵子树大小以便控制循环边界。 #### 边界条件处理 初始化时需考虑叶子节点的情况,一般设定其初始值为零或其他适当常数,具体取决于实际问题需求。另外还需注意特判仅有单个顶点的情形。 #### 复杂度分析 由于每次访问每条边两次(一次正向一次反向),因此时间复杂度大致为 O(n),这使得算法能够在合理时间内完成较大规模输入实例的运算。 #### 实现细节提示 - 使用邻接表存储图的信息; - 运用深度优先搜索遍历整棵树; - 记录每个节点及其对应子树内的最佳选择情况。 ```cpp #include <bits/stdc++.h> using namespace std; const int N = ...; vector<int> adj[N]; int dp[N]; void dfs(int u, int parent){ for(auto &v : adj[u]){ if(v != parent){ dfs(v, u); // 更新当前节点u的最佳决策 } } } int main(){ cin >> n >> m; while(m--){ int a,b; cin>>a>>b; adj[a].push_back(b); adj[b].push_back(a); } memset(dp, 0, sizeof(dp)); dfs(1,-1); // 假定第一个节点为根 cout << "最终结果:" << endl; return 0; } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值