文章目录
自守数、质数(素数)、约数、完全数
HJ99 自守数√
就是依次比较平方的尾数是否相等
自守数是指一个数的平方的尾数等于该数自身的自然数。例如:25^2 = 625,76^2 = 5776,9376^2 = 87909376。请求出n(包括n)以内的自守数的个数
输入:6
输出:4
说明:
有0,1,5,6这四个自守数
输入:1
输出:2
说明:
有0, 1这两个自守数
let line;
while ((line = readline())) {
let count = 0;
for (let i = 0; i <= parseInt(line); i++) {
if (String(Math.pow(i, 2)).endsWith(String(i))) count++;
}
console.log(count);
}
HJ6 质数因子√√
从2开始到Math.sqrt,能整除就一直除下去
输入:180
输出:2 2 3 3 5
function g(num) {
let res = [];
for (let i = 2; i <= Math.sqrt(num); i++) {
while (num % i === 0) {
res.push(i);
num /= i;
}
}
if (num > 1) res.push(num);
console.log(res.join(" "));
}
let num = parseInt(readline());
g(num);
HJ56 完全数计算√√
只要能被整除就是它的约数。
第一个完全数是6,循环从6开始。
统计约数从1开始。
完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数。
它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身。
例如:28,它有约数1、2、4、7、14、28,除去它本身28外,其余5个数相加,1+2+4+7+14=28。
输入n,请输出n以内(含n)完全数的个数。
输入:
1000
输出:
3
function g(num) {
let count = 0;
for (let i = 6; i <= num; i++) {
let sum = 0;
//统计因数的和,计数到该数的1/2即可
for (let j = 1; j <= i / 2; j++) {
if (i % j == 0) sum += j;
}
if (sum == i) count++;
}
console.log(count);
}
let num;
while ((num = parseInt(readline()))) {
g(num);
}
HJ60 查找组成一个偶数最接近的两个素数√
一分为二,每次两数加减1,判断两数是否为素数
素数判断,对于任意整数n,从2开始到Math.sqrt,能被整除则不是素数。
任意一个偶数(大于2)都可以由2个素数组成,组成偶数的2个素数有很多种情况,本题目要求输出组成指定偶数的两个素数差值最小的素数对。
输入:
20
输出:
7
13
输入:
4
输出:
2
2
function isSuShu(n) {
if (n < 2) return false;
let res = true;
for (let i = 2; i <= Math.sqrt(n); i++) {
if (n % i === 0) res = false;
}
return res;
}
function getRes(n) {
let a = n / 2;
let b = a;
while (a > 1 && b > 1) {
if (isSuShu(a) && isSuShu(b)) {
break;
} else {
a--;
b++;
}
}
return [a,b];
}
let line;
while ((line = readline())) {
let [a, b] = getRes(Number(line));
console.info(a);
console.info(b);
}
数列
HJ100 等差数列√
前n项和公式为:Sn=n*a1+n(n-1)d/2 或 Sn=n(a1+an)/2
等差数列 2,5,8,11,14。。。。
(从 2 开始的 3 为公差的等差数列)
输出求等差数列前n项和
输入:2
输出:7
说明:
2+5=7
输入:275
输出:113575
说明:
2+5+…+821+824=113575
let n = parseInt(readline());
let count = 2;
let last = 2;
for (let i = 1; i < n; i++) {
last += 3;
count += last;
}
console.info(count);
let n = parseInt(readline());
console.info(2 * n + (n * (n - 1) * 3) / 2);
let n = parseInt(readline());
let count = 2;
for (let i = 1; i < n; i++) {
count += 2 + i * 3;
}
console.info(count);
HJ76 尼科彻斯定理√√
前n项和公式为:Sn=n*a1+n(n-1)d/2 或 Sn=n(a1+an)/2
从1开始到pow,依次循环找出首项a1
验证尼科彻斯定理,即:任何一个整数m的立方都可以写成m个连续奇数之和。
例如:
1^3=1
2^3=3+5
3^3=7+9+11
4^3=13+15+17+19
输入:
6
输出:
31+33+35+37+39+41
let n = Number(readline());
let res = [];
let pow = Math.pow(n, 3);
for (let a1 = 1; a1 < pow; a1 += 2) {
if (n * a1 + n * (n - 1) === pow) {
res.push(a1);
for (let j = 1; j < n; j++) {
res.push(a1 + j * 2);
}
}
}
console.info(res.join("+"));
HJ35 蛇形矩阵
蛇形矩阵是由1开始的自然数依次排列成的一个矩阵上三角形。
例如,当输入5时,应该输出的三角形为:
1 3 6 10 15
2 5 9 14
4 8 13
7 12
11
输入:
4
输出:
1 3 6 10
2 5 9
4 8
7
let row = Number(readline());
let arr = [1];
for (var i = 2; i < row + 1; i++) {
arr.push(arr[arr.length - 1] + i);
}
for (var j = 0; j < row; j++) {
let str = "";
arr.slice(j).forEach((item) => {
str = str + (item - j) + " ";
});
console.log(str);
}
递归
HJ37 统计每个月兔子的总数√√
有一种兔子,从出生后第3个月起每个月都生一只兔子,小兔子长到第三个月后每个月又生一只兔子。
例子:假设一只兔子第3个月出生,那么它第5个月开始会每个月生一只兔子。
一月的时候有一只兔子,假如兔子都不死,问第n个月的兔子总数为多少?
输入:
3 第三个月
输出:
2 总共有2只兔子
斐波那契数列:1 1 2 3 5 8 13 21 34 f(n)=f(n-1)+f(n-2) n>2,n从0开始
递归,从大到小,带缓存
let n = Number(readline());
let cash = {};
function dp(n) {
if (cash[n]) return cash[n];
if (n < 3) return 1;
cash[n - 1] = dp(n - 1);
cash[n - 2] = dp(n - 2);
return cash[n - 1] + cash[n - 2];
}
console.info(dp(n));
let readline = require("readline");
let rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
function getSum(month) {
if (month < 3) {
return 1;
} else {
return getSum(month - 1) + getSum(month - 2);
}
}
rl.on("line", function (line) {
console.info(getSum(line));
});
HJ22 汽水瓶√√
输入:
3
10
81
0
输出:
1
5
40
说明:
样例 1 解释:用三个空瓶换一瓶汽水,剩一个空瓶无法继续交换
样例 2 解释:用九个空瓶换三瓶汽水,剩四个空瓶再用三个空瓶换一瓶汽水,剩两个空瓶,向老板借一个空瓶再用三个空瓶换一瓶汽水喝完得一个空瓶还给老板
function drink(n, result) {
if (n < 2) {
return result;
} else if (n === 2) {
return result + 1;
}
result += parseInt(n / 3);
let m = parseInt(n / 3) + (n % 3);
return drink(m, result);
}
let line;
while (line = readline()) {
let res = drink(Number(line), 0);
console.info(res);
}
递归,从大到小
小于2个瓶盖返回0瓶汽水,等于2个瓶盖返回1瓶汽水。
每次递归返回当前瓶盖能换回的汽水+下一次递归
function g(n) {
if (n < 2) return 0;
if (n === 2) return 1;
return parseInt(n / 3) + g(parseInt(n / 3) + (n % 3));
}
let line;
while ((line = readline())) {
let n = g(Number(line));
if (n) console.info(n);
}
HJ61 放苹果√√
把m个同样的苹果放在n个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?
注意:如果有7个苹果和3个盘子,(5,1,1)和(1,5,1)被视为是同一种分法。
输入:
7 3
输出:
8
递归,从大到小
// 递归
// 设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论,
// 当n>m(盘子大于苹果):必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m)
// 当n<=m(盘子小于等于苹果):不同的放法可以分成两类:
// 1、有至少一个盘子空着,即相当于f(m,n) = f(m,n-1);
// 2、所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n).
// 而总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n)
// 递归出口条件说明:
// 当n=1时,所有苹果都必须放在一个盘子里,所以返回1;
// 当没有苹果可放时,定义为1种放法;
// 递归的两条路,第一条n会逐渐减少,终会到达出口n==1;
// 第二条m会逐渐减少,因为n>m时,我们会return f(m,m) 所以终会到达出口m==0.
function count(apple, disk) {
if (apple === 0 || disk === 1) {
return 1;
} else if (disk > apple) {
return count(apple, apple);
} else {
return count(apple, disk - 1) + count(apple - disk, disk);
}
}
let [apple, disk] = readline().split(" ").map(Number);
console.info(count(apple, disk));
HJ91 走方格的方案数√
请计算n*m的棋盘格子(n为横向的格子数,m为竖向的格子数)从棋盘左上角出发沿着边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往上走
注:沿棋盘格之间的边缘线行走
输入:
2 2
输出:
6
递归,从上往下
// f(m, n) = f(m, n - 1) + f(m - 1, n)
const dp = (m, n) => {
if (m === 0 || n === 0) return 1;
// 目的地的上一步只可能是同一行的左侧或者同一列的上侧
return dp(m, n - 1) + dp(m - 1, n);
};
while ((line = readline())) {
const [n, m] = line.split(" ").map(Number);
console.log(dp(m, n));
}
余数
HJ53 杨辉三角的变形
以上三角形的数阵,第一行只有一个数1,以下每行的每个数,是恰好是它上面的数、左上角数和右上角的数,3个数之和(如果不存在某个数,认为该数就是0)。
求第n行第一个偶数出现的位置。如果没有偶数,则输出-1。例如输入3,则输出2,输入4则输出3,输入2则输出-1。
输入:
4
输出:
3
余数
//杨辉三角规律 行号 第一个偶数
// 1 1 -1
// 1 1 1 2 -1
// 1 2 3 2 1 3 2
// 1 3 6 7 6 3 1 4 3
// 1 4 10 16 19 16 10 4 1 5 2
// 1 5 15 30 45 51 45 30 15 5 1 6 4
//
// 首个偶数在该行第几个的规律: -1 -1 (2 3 2 4)···(2 3 2 4)
let res = [2, 3, 2, 4];
let line;
while ((line = readline())) {
let n = Number(line);
if (n <= 2) {
console.info(-1);
} else {
console.info(res[(n + 1) % 4]);
}
}
HJ55 挑7√(中级)
输出 1到n之间 的与 7 有关数字的个数。
一个数与7有关是指这个数是 7 的倍数,或者是包含 7 的数字(如 17 ,27 ,37 … 70 ,71 ,72 ,73…)
输入:
20
输出:
3
说明:
输入20,1到20之间有关的数字包括7,14,17共3个。
function getSevenCount() {
if (Number(line) < 7) return 0;
let res = 0;
for (let i = 7; i <= Number(line); i++) {
if (i % 7 === 0 || i.toString().split("").includes("7")) {
res++;
}
}
return res;
}
let line;
while ((line = readline())) {
console.info(getSevenCount(line));
}
HJ57 高精度整数加法√(中级)
输入:
9876543210
1234567890
输出:
11111111100
// 我们将两个字符串从尾到头进行处理
// 逐位相加,保留个位数字到结果字符串中,并记录进位的值在下一次循环中再次逐位相加
// 最终输出结果字符串
function sumBig(str1, str2) {
const a = str1.split("");
const b = str2.split("");
let c = 0;
let res = "";
while (a.length || b.length || c) {
c += ~~a.pop() + ~~b.pop(); // 个位求和
res = (c % 10) + res; // 记录
c = c > 9 ? 1 : 0; // 是否有进位
}
return res;
}
console.log(sumBig(readline(), readline()));
最大公约数、最小公倍数
HJ108 求最小公倍数√√
正整数A和正整数B 的最小公倍数是指 能被A和B整除的最小的正整数值
输入:5 7
输出:35
输入:2 4
输出:4
先求最大公约数,再求最小公倍数
最小公倍数 = (x*y)/最大公约数
function gongyue(a, b) {
while (a % b !== 0) {
let temp = a % b;
a = b;
b = temp;
}
return b;
}
function gongbei(a, b) {
return (a * b) / gongyue(a, b);
}
let [a, b] = readline().split(" ").map(Number);
console.info(gongbei(a, b));
最长回文子串
HJ85 最长回文子串√
输入:
cdabbacc
输出:
4
说明:
abba为最长的回文子串
找到回文中心点(BAB和BAAB两种),从里向外依次扩张
function g(l, r, str) {
while (l >= 0 && r < str.length && str[l] === str[r]) {
l--;
r++;
}
return r - l - 1;
}
function getMaxHuiLength(str) {
let max = 0;
for (let i = 0; i < str.length; i++) {
let res = g(i, i + 1, str);
max = Math.max(res, max);
res = g(i, i + 2, str);
max = Math.max(res, max);
}
return max;
}
let s = readline();
console.info(getMaxHuiLength(s));
暴力解,获取全部字串并依次判断是否为回文,同时不断更新最长的字串长度
function isHui(str) {
for (let i = 0; i < str.length; i++) {
if (str[i] != str[str.length - 1 - i]) {
return false;
}
}
return true;
}
function getMaxHuiLength(str) {
let currentHuiLength = 0;
let maxHuiLength = 0;
for (let i = 0; i < s.length; i++) {
for (let j = i; j < s.length; j++) {
let str = s.substring(i, j + 1);
if (isHui(str)) {
currentHuiLength = str.length;
}
maxHuiLength = Math.max(currentHuiLength, maxHuiLength);
}
}
return maxHuiLength;
}
let s = readline();
console.info(getMaxHuiLength(s));
HJ32 密码截取√√(中级)
示例1
输入:ABBA
输出:4
示例2
输入:ABBBA
输出:5
示例3
输入:12HHHHA
输出:4
// 密码截取
function getStringLength(key) {
let maxLength = 0;
let len = key.length;
for (let i = 0; i < len; i++) {
// 第一种情况 ABBA
var low = i; // 左指针
var high = i + 1; // 右指针
// 相等但无越界
while (low >= 0 && high < len && key[low] == key[high]) {
low--;
high++;
}
maxLength = Math.max(maxLength, high - low - 1);
// 第二种情况 ABBBA
low = i; // 左指针
high = i + 2; // 右指针
while (low >= 0 && high < len && key[low] == key[high]) {
low--;
high++;
}
maxLength = Math.max(maxLength, high - low - 1);
}
console.log(maxLength);
}
var line = readline();
getStringLength(line);
公共子串
HJ75 公共子串计算√(中级)
给定两个只包含小写字母的字符串,计算两个字符串的最大公共子串的长度。
注:子串的定义指一个字符串删掉其部分前缀和后缀(也可以不删)后形成的字符串。
输入:
asdfas
werasdfaswer
输出:
6
let str1 = readline();
let str2 = readline();
if (str1.length > str2.length) {
[str1, str2] = [str2, str1];
}
let max = 0;
for (let i = 0; i < str1.length; i++) {
for (let j = i + 1; j <= str1.length; j++) {
let s = str1.slice(i, j);
if (str2.indexOf(s) > -1) {
max = Math.max(max, s.length);
}
}
}
console.info(max);
HJ65 查找两个字符串a,b中的最长公共子串√(中级)
输入:
abcdefghijklmnop
abcsafjklmnopqrstuvw
输出:
jklmnop
// 总体思路:从短的字符串中取子串,看其在长字符串中是否存在
let s1 = readline();
let s2 = readline();
if (s1.length > s2.length) {
[s1, s2] = [s2, s1];
}
let length = 0;
let res = "";
for (let i = 0; i < s1.length; i++) {
for (let j = i + 1; j <= s1.length; j++) {
let sub = s1.slice(i, j);
if (s2.includes(sub)) {
if (j - i > length) {
res = sub;
length = j - i;
}
} else {
break;
}
}
}
console.info(res);
最长递增子序列(动态规划系列)一维dp数组
dp[i]表示以nums[i]这个数结尾的“最长递增子序列的长度”。
function lis(nums) {
// base case: dp数组全都初始化为1
let dp = Array(nums.length).fill(1);
for (let i = 0; i < nums.length; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
let res = Math.max(0, ...dp);
return res;
}
HJ103 Redraiment的走法√√(中级)
输入:
6
2 5 1 5 4 5
输出:
3
说明:
6个点的高度各为 2 5 1 5 4 5
如从第1格开始走,最多为3步, 2 4 5 ,下标分别是 1 5 6
从第2格开始走,最多只有1步,5
而从第3格开始走最多有3步,1 4 5, 下标分别是 3 5 6
从第5格开始走最多有2步,4 5, 下标分别是 5 6
所以这个结果是3。
function lis(nums) {
// base case: dp数组全都初始化为1
let dp = Array(nums.length).fill(1);
for (let i = 0; i < nums.length; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
let res = Math.max(0, ...dp);
return res;
}
let line;
while ((line = readline())) {
console.log(lis(readline().split(' ').map(Number)));
}
HJ24 合唱队√√(中级)
输入:
8
186 186 150 200 160 130 197 200
输出:
4
说明:
由于不允许改变队列元素的先后顺序,所以最终剩下的队列应该为186 200 160 130或150 200 160 130
最长
let num;
while ((num = readline())) {
//186 186 150 200 160 130 197 200
const arr = readline().split(" ").map(Number);
//1,1,1,2,2,1,3,4
let dp1 = lis(arr);
//3,3,2,3,2,1,1,1
let dp2 = lis(arr.reverse()).reverse();
let max = 0;
for (let i = 0; i < dp1.length; i++) {
max = Math.max(max, dp1[i] + dp2[i] - 1);
}
console.log(arr.length - max);
}
function lis(nums) {
// base case: dp数组全都初始化为1
let dp = Array(nums.length).fill(1);
for (let i = 0; i < nums.length; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
return dp;
}
最大子数组(动态规划系列)
输入nums = [-3,1,3,-1,2,-4,2],算法返回5
因为最大子数组[1,3,-1,2]的和为5
dp[i]表示以nums[i]这个数结尾的“最大子数组和”。
let num;
while ((num = readline())) {
let res = maxSubArray(num.split(" ").map(Number));
console.info(res);
}
function maxSubArray(nums) {
let n = nums.length;
if (n === 0) return 0;
// base case
// 第一个元素面前没有子数组
let dp = Array(n);
dp[0] = nums[0];
// 状态转移方程
for (let i = 1; i < n; i++) {
dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]);
}
let res = Math.max(...dp);
return res;
}
最长公共子序列(动态规划系列)二维dp数组
输入str1=“abcde”, str2=“aceb”, 算法应该输出3,
因为str1和str2的最长公共子序列是"ace",它的长度是3。
function longestCommonSubsequence1(str1, str2) {
let dp = function (i, j) {
if (i === -1 || j === -1) return 0;
if (str1[i] === str2[j]) {
return dp(i - 1, j - 1) + 1;
} else {
return Math.max(dp(i - 1, j), dp(i, j - 1));
}
};
return dp(str1.length - 1, str2.length - 1);
}
function longestCommonSubsequence2(str1, str2) {
let m = str1.length;
let n = str2.length;
let dp = Array(m + 1)
.fill()
.map((item) => Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (str1[i - 1] === str2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
return dp[m][n];
}
let res1 = longestCommonSubsequence1("abcde", "aceb");
console.info(res1);
let res2 = longestCommonSubsequence2("abcde", "aceb");
console.info(res2);
动态规划
HJ52 计算字符串的编辑距离(中级)二维dp数组
Levenshtein 距离,又称编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。编辑距离的算法是首先由俄国科学家 Levenshtein 提出的,故又叫 Levenshtein Distance 。
例如:
字符串A: abcdefg
字符串B: abcdef
通过增加或是删掉字符 ”g” 的方式达到目的。这两种方案都需要一次操作。把这个操作所需要的次数定义为两个字符串的距离。
要求:
给定任意两个字符串,写出一个算法计算它们的编辑距离。
输入:
abcdefg
abcdef
输出:
1
dp table
function minDistance(word1, word2) {
let len1 = word1.length;
let len2 = word2.length;
let dp = Array(len1 + 1)
.fill()
.map((item) => Array(len2 + 1).fill(0)); //dp数组
for (let i = 1; i <= len1; i++) dp[i][0] = i;
for (let j = 1; j <= len2; j++) dp[0][j] = j;
for (let i = 1; i <= len1; i++) {
for (let j = 1; j <= len2; j++) {
if (word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = Math.min(
dp[i - 1][j] + 1, //插入
dp[i][j - 1] + 1, //删除
dp[i - 1][j - 1] + 1 //替换
);
}
}
}
return dp[len1][len2];
}
let line;
while ((line = readline())) {
console.info(minDistance(line, readline()));
}
最长回文子序列(动态规划系列)二维dp数组
输入"aecda",返回3,因为最长回文子序列为"aca"
function longestPalindromeSubsequence(str) {
let n = str.length;
let dp = Array(n)
.fill()
.map((item) => Array(n).fill(0));
for (let i = 0; i < n; i++) dp[i][i] = 1;
for (let i = n - 2; i >= 0; i++) {
for (let j = i + 1; j <= n; j++) {
if (str[i] === str[j]) {
dp[i][j] = dp[i - 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
return dp[0][n - 1];
}
longestPalindromeSubsequence("aecda");
子串
HJ63 DNA序列√(中级)
输入:
ACGT
2
输出:
CG
说明:
ACGT长度为2的子串有AC,CG,GT3个,其中AC和GT2个的GC-Ratio都为0.5,CG为1,故输出CG
示例2
输入:
AACTGTGCACGACCTGA
5
输出:
GCACG
说明:
虽然CGACC的GC-Ratio也是最高,但它是从左往右找到的GC-Ratio最高的第2个子串,所以只能输出GCACG。
const line = readline();
const n = Number(readline());
const l = line.length;
let max = 0; // 最大出现数量(比例)
let result; // 输出的子串
for (let i = 0; i <= l - n; i++) {
const cut = line.slice(i, i + n); // 根据长度截取的子串
let timer = 0; // 初始化 GC 出现次数
for (let j of cut) {
if (j === "G" || j === "C") {
timer++;
}
}
if (timer > max) {
result = cut; // 更新输出的子串
max = timer; // 更新最大数量(比例)
}
}
console.log(result);