问题描述
小F和他的朋友们围成了一个圈,他们每个人背上贴了一个字母,要么是A,要么是B。从第一个人开始计数,每当他遇到一个贴着字母A的人就计数一次。当他计数到第kk个A时,任务就结束了。你需要帮助小F计算一下,他总共需要数多少个人才能在第kk次遇到A后停止。保证至少有一个人背上贴的是A。
测试样例
样例1:
输入:
n = 3 ,k = 3 ,people = "AAB"
输出:4
样例2:
输入:
n = 3 ,k = 3 ,people = "BBA"
输出:9
样例3:
输入:
n = 5 ,k = 4 ,people = "ABABA"
输出:6题目解析
思路
这道题目的核心思路是模拟在围成圈的人群中按顺序进行计数的过程,从第一个人开始,依次查看每个人背上贴的字母,每当遇到字母 “A” 时就对遇到 “A” 的次数进行累加,持续这个过程,直到累计遇到 “A” 的次数达到给定的第 次为止,在此过程中同时记录总共数过的人数,最终将这个总人数作为结果返回。
由于人群是围成一个圈的,所以需要通过取余操作来实现循环遍历每个人,保证计数过程能够在这个环形结构中持续进行下去。例如,当索引到达最后一个人后,下一次索引要回到第一个人重新开始计数,就利用索引对总人数 取余来巧妙实现这种循环的效果。
拿样例 1 来说,总共有 3 个人,背上的字母是 “AAB”,要数到第 3 个 “A”。开始计数,先数第一个人,字母是 “A”,遇到 “A” 的次数就加 1,此时遇到 “A” 的次数为 1,接着数第二个人,字母是 “A”,遇到 “A” 的次数再加 1 变为 2,继续数第三个人,字母是 “B”,不增加遇到 “A” 的次数,然后又循环回到第一个人,字母是 “A”,遇到 “A” 的次数达到了 3,满足条件,此时总共数过的人数就是 4 人,这就是最终的结果。
public class Main {
public static int solution(int n, int k, String people) {
int countA = 0; // 已遇到的A的次数
int totalPeople = 0; // 计数的总人数
int index = 0; // 当前计数的人在数组中的索引
// 循环计数直到遇到第k个A
while (countA < k) {
totalPeople++; // 计数一个人
if (people.charAt(index) == 'A') {
countA++; // 如果是A,累计次数
}
index = (index + 1) % n; // 循环移动到下一个人
}
return totalPeople;
}
public static void main(String[] args) {
System.out.println(solution(3, 3, "AAB") == 4); // 样例1
System.out.println(solution(3, 3, "BBA") == 9); // 样例2
System.out.println(solution(5, 4, "ABABA") == 6); // 样例3
}
}
代码详解
在 solution 方法中,首先定义了三个变量。countA 用于记录已经遇到的字母 “A” 的次数,初始化为 0;totalPeople 用来统计计数的总人数,同样初始化为 0;index 表示当前计数的人在字符串表示的人群(也就是 people 变量)中的索引位置,初始化为 0。
接着进入一个 while 循环,循环的条件是 countA < k ,也就是只要还没有数到第 个 “A”,就持续循环计数。在循环内部,首先执行 totalPeople++ ,意味着每查看一个人就对总人数计数加 1。然后通过 people.charAt(index) 获取当前索引位置对应的人背上的字母,如果这个字母是 “A”,就执行 countA++ ,对遇到 “A” 的次数进行累加。
关键的一步是更新索引 index ,通过 (index + 1) % n 操作,让索引能够循环地在 0 到 之间变化(其中 是总人数),模拟在围成圈的人群中依次往后移动的效果。当 while 循环结束,也就是已经数到了第 个 “A” 时,函数返回记录的总人数 totalPeople 。
在 main 方法中,给出了几个不同输入情况的测试样例,通过调用 solution 方法并对比输出结果与预期值是否相等,以此来验证代码的正确性,这种测试方式有助于及时发现代码在处理不同人数、不同字母排列以及不同目标计数次数等多种情况时可能存在的逻辑问题,确保代码能准确地计算出符合要求的总人数。
知识总结
通过刷这道题,我们收获了一些重要的知识点以及更深层次的理解。
一方面,对循环和取余操作在模拟环形结构场景中的运用有了更清晰的认识。在编程中,经常会遇到需要模拟环形或者周期性的情况,像本题中人群围成圈的计数就是典型例子。通过取余操作结合循环,能够轻松地在代码里实现这种环形遍历的逻辑,而不需要复杂的额外判断语句来处理循环回绕的问题。理解这种运用方式后,在处理如时钟指针转动、循环队列等类似的环形结构相关问题时,我们就能更迅速地构建起有效的代码逻辑。
另一方面,对于字符串中字符的访问和判断操作更加熟练了。本题需要不断地从表示人群的字符串里获取每个人对应的字母,通过 charAt 方法来实现字符的访问,并依据获取到的字符进行条件判断,这是字符串操作中很常见且基础的部分。在很多文本处理、数据验证等编程场景中,熟练掌握字符的访问和判断操作是非常关键的,比如判断密码是否符合特定格式、统计文本中特定字符出现的频率等都离不开这些操作。
对于入门的同学,我的学习建议是,首先要扎实掌握循环语句(像 for 、while 等)以及取余运算的基本概念和用法,多去做一些简单的练习,比如模拟时钟时针每走一圈时针走过的小时数计算,通过这种小练习来体会循环和取余配合使用的妙处。对于字符串操作,要深入学习字符串的各种方法,包括创建、拼接、截取以及字符的访问和比较等,尝试自己编写一些小程序,例如统计输入字符串中元音字母的个数等,以此来熟练运用这些操作,增强对字符串处理的能力。
学习计划
结合刷题功能,以下是我总结的高效学习方法。
制定刷题计划
我们可以按照知识点类型和难度层次来制定刷题计划。对于像本题涉及的循环与取余操作以及字符串处理这类基础知识点相关的题目,可以先从简单的只运用单一知识点的题目开始刷起。比如先刷一些仅考查循环语句基本运用,像计算从 1 到某个数的累加和的题目,或者只涉及简单字符串字符访问判断的题目,如判断一个字符串是否全是大写字母等。掌握好基础运用后,再去做结合了这两个知识点,且逻辑稍复杂些的综合性题目,例如在一个循环结构里对字符串进行特定规则的字符筛选并统计数量等题目。每周可以设定一个合理的刷题量,比如每周刷 10 - 12 道题,根据题目难度合理分配时间,确保每道题都能认真理解并掌握解题思路,循序渐进地提升自己的编程水平。
利用错题进行针对性学习
错题是我们学习过程中的宝贵财富,当遇到做错的题目时,要仔细分析原因。如果是循环与取余操作方面的错误,比如忘记了取余的规则或者循环的终止条件设置错误,那就重新回顾循环和取余运算的相关知识点,查阅资料或者参考教材进行复习巩固,并且做一些针对性的小练习来强化记忆,比如针对不同的循环终止条件去编写小程序,或者做一些取余运算结果判断的专项练习。要是字符串操作方面的问题,比如 charAt 方法使用错误或者字符判断逻辑有误,就需要再次深入学习字符串操作的知识点,对照正确答案分析自己代码的问题所在,然后找几道类似的题目进行练习,确保下次不再犯同样的错误。
工具运用
要想将 AI 刷题功能与其他学习资源相结合,达到更好的学习效果,可以从以下方面入手。
一方面,结合线上的编程课程学习。很多在线编程课程都有关于循环语句、取余运算以及字符串处理等基础知识的详细讲解,还有丰富的案例演示和课后练习。在刷题之前,如果对相关知识点掌握不扎实,就可以去对应的课程里进行学习巩固,跟着老师的思路和示例代码深入理解概念和运用方法。在刷题过程中遇到难题时,也可以参考课程里类似问题的解法思路,拓宽自己的思维方式,然后再尝试用自己的方法去解决题目,这样能更好地吸收知识并且提高解题能力。
另一方面,利用编程论坛和社区也是不错的选择。在论坛上可以搜索到其他程序员对于类似本题这种涉及循环、取余以及字符串处理问题的讨论,看看大家分享的不同解法思路、遇到的问题以及解决办法等,从中获取灵感和经验。同时,自己也可以把刷题过程中遇到的疑惑或者独特的解题思路发布出来,与大家交流互动,通过他人的反馈来进一步完善自己的知识体系和解题方法,而且在交流过程中还能结交志同道合的学习伙伴,一起互相鼓励、共同进步。