打印字符串
给定一个字符串,请你编写一个函数,void print(char str[])
,将这个字符串打印出来。
输入格式
共一行,包含一个字符串。
输出格式
共一行,表示打印出的字符串。
数据范围
1≤字符串长度≤100
输入样例:
I love AcWing.
输出样例:
I love AcWing.
我们要向一个函数里输入一个字符串,要用到void print(char str[])
,又由于我们是要输入一个句子,所以可能会有空格,故而我们用fgets
函数输入
代码如下:
#include<iostream>
using namespace std;
void print(char str[]){
printf("%s",str);
}
int main()
{
char str[101];
fgets(str,101,stdin);
print(str);
return 0;
}
Java的话要带有空格要用nextline
import java.util.Scanner;
public class Main {
private static void print(char[] str) {
for (char c: str)
System.out.print(c);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
print(str.toCharArray());
}
}
数组去重
给定一个长度为 n 的数组 a,请你编写一个函数:
int get_unique_count(int a[], int n)
; // 返回数组前n个数中的不同数的个数
输入格式
第一行包含一个整数 n。
第二行包含 n 个整数,表示数组 a。
输出格式
共一行,包含一个整数表示数组中不同数的个数。
数据范围
1≤n≤1000,
1≤ai≤1000。
输入样例:
5
1 1 2 4 5
输出样例:
4
对于此题,我们只需要依次枚举每一个数,对于每一个被枚举的数我们依次遍历它前面的每个数,如果要是一样就标记他之前出现过,如果没有出现过就让答案加一
代码如下:
#include<iostream>
using namespace std;
int get_unique_count(int a[], int size){
int cnt=0;
for(int i=0;i<size;i++){
bool appear=false;
for(int j=0;j<i;j++){
if(a[j]==a[i]){
appear=true;
break;
}
}
if(!appear)cnt++;
}
return cnt;
}
int main()
{
int a[1010];
int size;
cin>>size;
for(int i=0;i<size;i++){
cin>>a[i];
}
cout<<get_unique_count(a,size)<<endl;
return 0;
}
Java与此类似
代码如下:
import java.util.Scanner;
public class Main {
private static int getUniqueCnt(int a[], int n) {
int res = 0;
for (int i = 0; i < n; i ++ ) {
boolean flag = true;
for (int j = 0; j < i; j ++ ) {
if (a[j] == a[i]) {
flag = false;
break;
}
}
if (flag) res ++ ;
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
for (int i = 0; i < n; i ++ )
a[i] = sc.nextInt();
System.out.println(getUniqueCnt(a, n));
}
}
走方格
给定一个 n×m 的方格阵,沿着方格的边线走,从左上角 (0,0) 开始,每次只能往右或者往下走一个单位距离,问走到右下角 (n,m) 一共有多少种不同的走法。
输入格式
共一行,包含两个整数 n 和 m。
输出格式
共一行,包含一个整数,表示走法数量。
数据范围
1≤n,m≤10
输入样例:
2 3
输出样例:
10
这个题要用到递归的思路,以2*3为例,共有十种方案
探索方式如图所示
故而我们的dfs的方式是如果要是到了那个目标位置就停止,让答案加一,如果不是就让它向右移一个或是向下移一格
代码如下:
#include<iostream>
using namespace std;
int n,m,ans=0;
int dfs(int x,int y){
if(x==n&&y==m){
ans++;
}else{
if(x<n)dfs(x+1,y);
if(y<m)dfs(x,y+1);
}
}
int main()
{
cin>>n>>m;
dfs(0,0);
cout<<ans<<endl;
return 0;
}
如果是Java
可以反过来想,方案数的和就是有终点向左或上走的方案数之和,及数学归纳法
import java.util.Scanner;
public class Main {
private static int dfs(int n, int m) {
if (n == 0 && m == 0) return 1;
if (n < 0 || m < 0) return 0;
return dfs(n - 1, m) + dfs(n, m - 1);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
System.out.println(dfs(n, m));
}
}
再比如说对于这个递归,我们可以这样想
还是以c++代码为例来解释递归:
#include<iostream>
using namespace std;
int n,m,ans=0;
int dfs(int x,int y){
if(x==n&&y==m){
ans++;
}else{
if(x<n)dfs(x+1,y);
if(y<m)dfs(x,y+1);
}
}
int main()
{
cin>>n>>m;
dfs(0,0);
cout<<ans<<endl;
return 0;
}
刚开始进行第一次递归时,我们可以发现,他会先向右递归,也就是会走到这儿:
也就是这个小人的位置,然后他就是会重新走一遍那个函数,先判断结果不成立,他不是答案的坐标,然后就再次走到了我们的调用自己的函数,我们可以发现,他下一次是走到了这儿:
也就是现在这个小人的位置,然后以此类推,下一次应该走到这儿
此时我们发现:
也就是应该走if(y<m)dfs(x,y+1);
步
也就是走到这儿:
以此类推,然后最终可以走到终点
走到这儿之后就可以让答案加一,我们就满足了
但是我们return到哪里了呢?我们return到了我们上一次用它的地方,也就是
至于语句,我们是从这里进去的
这样一直返回下去,直到退回这里时:
情况发生了变化,因为这里是从if(x<n)dfs(x+1,y);
进来的,也就会从这个语句出去,也就是:
下一步要执行
语句,也就是会走到
也就是开启了下一个答案的探索,以此类推,就是这道题目的递归全过程
这里不需要记录的原因是递归每次每层函数结束时,都会return返回至上一次被调用的位置,返回后,要么执行后面的语句,要么不满足后面的语句条件或者走到函数尾端返回至上一次调用它的位置,所以每一次的探索都是不一样的,至于最后一次是怎么返回而不是重复走的。。。。。
到了最后一次的答案探索,如果要是以次代码的话应该是这条:
此时再返回时
此三点均是返回至上一次调用的时候,也就是
下一步该走 if(y<m)dfs(x,y+1);
但是他不满足判断条件因为此时y=m
故而直接跑到函数末尾,也就是return,返回至上一次掉用它的地方
就到这儿三个点
可是这三个点均是由if(y<m)dfs(x,y+1);
进入下一层函数的,故而出来时都是在这儿:
也就是蓝色位置,此时已经函数尾端,没有别的语句,只能return,此时只能再次return会上一层函数,这样,到最后就会跳出函数,返回到主函数的输出那一步,也就结束了递归。
大家可以自己画画图,一步一步走走看
替换空格
请实现一个函数,把字符串中的每个空格替换成"%20
"。
数据范围
0≤ 输入字符串的长度 ≤1000。
注意输出字符串的长度可能大于 1000。
样例
输入:"We are happy."
输出:"We%20are%20happy."
我们可以对一个字符串来看,遍历一个字符串,当它遇到空格时就加上题目要求的东西
代码如下:
class Solution {
public:
string replaceSpaces(string &str) {
string s;
for(auto c:str){
if(c==' ')s+="%20";
else s+=c;
}
return s;
}
};
或者我们可以用双指针算法
在部分编程语言中,我们可以动态地将原数组长度扩大,此时我们就可以使用双指针算法,来降低空间的使用:
首先遍历一遍原数组,求出最终答案的长度length
;
将原数组resize
成length
大小;
使用两个指针,指针i指向原字符串的末尾,指针j指向length
的位置;
两个指针分别从后往前遍历,如果str[i] == ' '
,则指针j的位置上依次填充'0', '2', '%'
,这样倒着看就是"%20
";如果str[i] != ' '
,则指针j的位置上填充该字符即可。
由于i
之前的字符串,在变换之后,长度一定不小于原字符串,所以遍历过程中一定有i <= j
,这样可以保证str[j]
不会覆盖还未遍历过的str[i]
,从而答案是正确的。
时间复杂度分析
原字符串只会被遍历常数次,所以总时间复杂度是 O(n)
代码如下:
class Solution {
public:
string replaceSpaces(string &str) {
int len = 0;
for (auto c : str)
if (c == ' ')
len += 3;
else
len ++ ;
int i = str.size() - 1, j = len - 1;
str.resize(len);
while (i >= 0)
{
if (str[i] == ' ')
{
str[j -- ] = '0';
str[j -- ] = '2';
str[j -- ] = '%';
}
else str[j -- ] = str[i];
i -- ;
}
return str;
}
};
如果是Java
直接用函数API即可
class Solution {
public String replaceSpaces(StringBuffer str) {
return str.toString().replace(" ", "%20");
}
}