题解:学而思编程 特殊数字2

【题目来源】

学而思编程:特殊数字2

【题目描述】

如果一个十进制正整数能够被它的每一个非零数码整除,则称它为特殊数字,比如 \(408\) 是一个特殊数字,因为 \(408\) 能被 \(4\)\(8\) 整除,而 \(1023\) 不是,因为它不能被 \(2\) 整除。给定一个正整数 \(n\),请判定它是否为特殊数字。

【输入】

第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。

每组数据占一行,包含一个整数 \(n\)

【输出】

每组数据输出一行结果,如果 \(n\) 是一个特殊数字,输出 Yes;否则,输出 No。

【输入样例】

3
13
1
1024

【输出样例】

No
Yes
Yes

【核心思想】

  1. 问题分析:给定正整数 \(n\),判断其是否为"特殊数字"——即 \(n\) 能被其每一个非零数码整除。例如 \(408\) 是特殊数字(\(408 \div 4 = 102\)\(408 \div 8 = 51\)),\(1023\) 不是(\(1023 \div 2 = 511.5\))。这是一个大数取模 + 逐位验证问题,关键在于处理 \(n\) 可能非常大的情况,以及高效判断整除性。

  2. 算法选择

    • 字符串存储:将 \(n\) 作为字符串读入,避免整数溢出
    • 大数取模:逐位模拟大数对单个数码取模,\(O(|n|)\) 完成
    • 出现标记:用布尔数组 vis[10] 标记哪些非零数码在 \(n\) 中出现,避免重复检查
  3. 关键步骤

    • 初始化:读取 \(T\)(测试组数)

    • 处理每组数据

      • 读取数字字符串 \(s\)\(n = s.size()\)
      • memset(vis, false, sizeof(vis))
      • 标记出现数码:遍历 \(i\)\(0\)\(n-1\)vis[s[i] - '0'] = true
      • 验证整除性
        • flag = true
        • 遍历 \(i\)\(1\)\(9\)
          • vis[i] == truechk(i) == falseflag = falsebreak
      • flag == true:输出 Yes,否则输出 No
    • 大数取模函数 chk(x)

      • num = 0
      • 遍历 \(i\)\(0\)\(n-1\)
        • num = num * 10 + (s[i] - '0')
        • num %= x:每一步取模防止溢出
      • num == 0:返回 true(可整除),否则返回 false
  4. 时间/空间复杂度

    • 时间复杂度:\(O(T \times |n| \times 9)\),每组数据最多检查 \(9\) 个数码,每次取模 \(O(|n|)\)
    • 空间复杂度:\(O(|n|)\),存储字符串和标记数组
  5. 大数逐位取模的核心思想

    • 模运算性质\((a \times 10 + b) \bmod x = ((a \bmod x) \times 10 + b) \bmod x\),允许逐位计算大数对 \(x\) 的模,无需存储完整数值
    • 非零数码筛选\(0\) 不能作为除数,且 \(0\) 对整除性无约束(任何数都能被 \(0\) 整除无意义),因此只检查 \(1\)\(9\)
    • 出现标记去重:同一数码多次出现只需验证一次,vis 数组避免重复计算
    • 提前终止:一旦发现某个出现过的数码不能整除 \(n\),立即判定为 No,无需继续检查
    • 适用于"大数整除性判定 + 数码约束"类问题,逐位取模将大数运算转化为线性扫描

【算法标签】

字符串入门

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, T;
string s;
bool vis[10];  // 记录数字0-9是否在字符串中出现

// 检查字符串表示的数字是否能被x整除
bool chk(int x)
{
    int num = 0;
    for (int i = 0; i <= n - 1; i++)  // 遍历字符串的每个字符
    {
        num = num * 10 + (s[i] - '0');  // 模拟大数计算
        num %= x;  // 取模,防止溢出
    }
    if (num == 0)  // 如果余数为0,说明能被整除
    {
        return true;
    }
    return false;
}

int main()
{
    cin >> T;  // 读取测试用例数量

    while (T--)  // 处理每个测试用例
    {
        cin >> s;  // 读取数字字符串
        n = s.size();  // 获取字符串长度

        memset(vis, false, sizeof(vis));  // 重置vis数组

        // 记录哪些数字在字符串中出现了
        for (int i = 0; i <= n - 1; i++)
        {
            vis[s[i] - '0'] = true;  // 标记数字出现
        }

        bool flag = true;  // 标志是否所有出现的数字都能整除这个数

        // 检查1-9中每个在字符串中出现的数字
        for (int i = 1; i <= 9; i++)
        {
            if (vis[i] == true && chk(i) == false)  // 如果数字i出现且不能整除
            {
                flag = false;  // 标记为不满足条件
                break;
            }
        }

        if (flag == true)
        {
            cout << "Yes" << endl;
        }
        else
        {
            cout << "No" << endl;
        }
    }

    return 0;
}

【运行结果】

3
13
No
1
Yes
1024
Yes
posted @ 2026-07-04 15:06  团爸讲算法  阅读(7)  评论(0)    收藏  举报