給定正整數 N,找出大於 N 的最小整數,使其十進制表示中**任意相鄰兩位數字不同** (稱為「不重複數」)。例如 1101 重複 (相鄰 1,1),1201 不重複。暴力遞增檢查在大數下極慢,本文提出 O(位數) 的貪心 + 進位演算法。
typedef struct {
unsigned char d[20]; // 足夠容納 64 位整數
int top; // 最高有效位索引
} Map;
#include <stdio.h>
typedef struct { unsigned char d[20]; int top; } Map;
unsigned combi(const Map *m) {
unsigned n = 0;
for (int i = m->top; i >= 0; i--) n = n * 10 + m->d[i];
return n;
}
void clear(Map *m, int from, int to) {
for (int i = from - 1; i >= to; i--) m->d[i] = 0;
}
void add_1(Map *m, int from) {
m->d[from]++;
for (int i = from; i < m->top; i++) {
m->d[i+1] += m->d[i] / 10;
m->d[i] %= 10;
}
if (m->d[m->top] > 9) {
m->d[m->top+1] = m->d[m->top] / 10;
m->d[m->top++] %= 10;
}
}
int search(const Map *m) {
for (int i = m->top; i > 0; i--)
if (m->d[i] == m->d[i-1]) return i - 1;
return -1;
}
void parse(Map *m, unsigned n) {
m->top = -1;
while (n) { m->d[++m->top] = n % 10; n /= 10; }
}
unsigned find(unsigned n) {
Map m; int end = 0, pos;
parse(&m, n);
while ((pos = search(&m)) > -1) {
add_1(&m, pos);
clear(&m, pos, end);
end = pos;
}
return combi(&m);
}
int main() {
for (unsigned i = 19922884; i < 19922885; i++)
printf("%u %u\n", i, find(i + 1));
return 0;
}
此演算法展示了「從右向左掃描、就地進位、低位填最小模式」的通用技巧,可推廣至「無連續三位相同」、「特定數字不可相鄰」等變體問題。