【数论】3260. 找出最大的 N 位 K 回文数|2370
本文涉及知识点
C++动态规划
数论:质数、最大公约数、菲蜀定理
LeetCode3260. 找出最大的 N 位 K 回文数
给你两个 正整数 n 和 k。
如果整数 x 满足以下全部条件,则该整数是一个 k 回文数:
x 是一个 回文数。x 可以被 k 整除。以字符串形式返回 最大的 n 位 k 回文数。
注意,该整数 不 含前导零。
示例 1:
输入: n = 3, k = 5
输出: “595”
解释:
595 是最大的 3 位 k 回文数。
示例 2:
输入: n = 1, k = 4
输出: “8”
解释:
1 位 k 回文数只有 4 和 8。
示例 3:
输入: n = 5, k = 6
输出: “89898”
提示:
1 <= n <= 105
1 <= k <= 9
数论
v1[i]记录 10i%k,x=其和乘以9%k就是最大n位数%k,最大的n位数是各位都是9。如果x等于0,最大的9位数就是本题答案。
v1[0]=1%k,v1[i] = (v1[i-1]*10)%k。
x ∈ \in ∈ [0,k] ,而k<=9。
错误性质一:如果n是奇数,将最中间的9减少x,就是回文,且被k整除。错误原因:中间的减少x,结果不一定减少x,而是x × \times × v[n/2]。
n2=(n+1)/2。由于左右部分完全对称,故我们只需要枚举左半部分减少了i。但这个时间复杂度:O(10n2),超时。
动态规划
我们令整个字符串为ans,左半部分为s。
动态规划的状态表示
dp[i][j] 记录s长度为i的后缀,除最左的位外可以减少任意次,最左的位最少减少多少次使得str总共减少%k == j。空间复杂度:O(nk)
动态规划的填表顺序
枚举前置状态和当前状态。i = 0 to i<n2
动态规划的填报顺序
如果dp[i][j]是10,忽略。
int t = v1[i1];if (i1 != i2) { t += v1[i2]; } t = (t*m + j) % k;
i 是s 从右向左的下标,从0开始。其在左半部分对应ans[j1],右半部分对应ans[j2]。
MinSelf(dp[i+1][t] , m) 更新dp1的时候,同时更新dp2 dp2[i+1][t] = j
动态规划的初始值
dp[0][0]=0,其它全为10。
动态规划的返回值
dp[i+1][x] < 10结束 fun(i+1,x)
fun(p,x)
q =p to 1
m = dp[q][x]
s 倒数第q-1位 减少m,str右半部分也要做响应减少。
x = dp2[q][x]
代码
class Solution {public:string largestPalindrome(int n, int k) {vector<int> v1(n,1%k);for (int i = 1; i < n; i++) {v1[i] = v1[i - 1] * 10 % k;}const int x = accumulate(v1.begin(), v1.end(), 0)*9 % k;if (0 == x) { return string(n,'9'); }const int n2 = (n+1) / 2; vector<vector<int>> dp(n2 + 1, vector<int>(k, 10));auto dp2 = dp;dp[0][0] = 0;int i1 = n2-1;int i2 = n/2;for (int i = 0; i < n2;i++,i1--,i2++) {for (int j = 0; j < k; j++) {if (10 == dp[i][j]) { continue; }for (int m = 0; m < k; m++) {int t = v1[i1];if (i1 != i2) { t += v1[i2]; } t = (t*m + j) % k;if (m < dp[i + 1][t]){dp[i + 1][t] = m;dp2[i + 1][t] = j;} }}if (dp[i + 1][x] < 10) {string ans(n2, '9');auto DoS = [&](int p, int x) { for (; p > 0; p--) {int m = dp[p][x]; ans[n2- 1 - (p-1)] -= m;x = dp2[p][x];}}; DoS(i + 1, x);string right(ans.begin(), ans.begin() + (n - n2));ans += string(right.rbegin(), right.rend());return ans;}}return "";}};
核心代码
int n, k;TEST_METHOD(TestMethod11){n = 3, k = 5;auto res = Solution().largestPalindrome(n, k);AssertEx(string("595"), res);}TEST_METHOD(TestMethod12){n = 1, k = 4;auto res = Solution().largestPalindrome(n, k);AssertEx(string("8"), res);}TEST_METHOD(TestMethod13){n = 5, k = 6;auto res = Solution().largestPalindrome(n, k);AssertEx(string("89898"), res);}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。