旋转数字

题目描述
小明发现了一个很好玩的事情,他对一个数作旋转操作,把该数的最后的数字
移动到最前面。比如,数 123 可以得到 312,231,123,这样就可以得到很多
个数。  现在,小明的问题是这些数中,有多少个不同的数小于原数,多少个等于原数,
多少个大于原数。  旋转中可能会出现前导零,两数比较的时候可以忽略前导零的影响。  输入格式
 输入一个整数 N(0< N≤10^100000)。
 输出格式
 答案在一行中输出三个整数,分别是小于 N,等于 N,大于 N 的个数,中间
以空格隔开。
 样例输入
 341
 样例输出
 1 1 1

初步分析
如果暴力做在这道题的话复杂度为O(N^2)对于比较大的数据的话肯定会卡住。所以得另寻道路。
我们不妨先用一个例子来找找有没有什么简便的方法。

设字符串为12312312,将最后一个字母放在最前面时:21231231.(2与1比较)。当后面两个放到最前面时12123123(前面12都是相同的,直接比较第3个)。现在我们想象扩展KMP里面的next数组的定义next[i]表示T[i…n-1]与T的最大公共前缀。那这不就是我们移动之后所进行的从第i个开始与第1个开始依次匹配嘛。很显然前面的next[i]次匹配都是相同的(想想next的定义).所以我们直接比较第一个不相同的字符就行了。无非比它大或者比它小,只比较依次就行了。(比较str[(i+next[i])%len]与str[next[i]%len]的大小)所以整体的复杂度直接降到了O(N)。

用例优化
我们再想想,还有没有情况可以优化的呢。就是可以把复杂度降到O(N)以下的情况。
是有的哦,当这个字符串是由一个最小子串重复出现的时候。比如abcabcabcabc…这种情况。我们需要比较N次吗。没必要,很容易就想到只需要比较移动最后一组最小子串的情况就行了。其他组子串的匹配结果与最后一组子串的结果是一样的。所以最后的结果全部乘以组数就行了。

那么怎么检查一个字符串是不是又一个最小子串重复得到的呢。
我们需要用到KMP里面的fail数组(有的地方也称之为next数组)。先说结论int mix_n=n-fail[n-1]-1;
if(n%min_n == 0 ) 这个字符串的最小重复子串的长度就是min_n.其实这个结论很简单推导的。比如 T a b c a b c a b c…
i: 0 1 2 3 4 5 6 7 8…
fail[i] -1 -1 -1 0 1 2 3 4 5…
只有最前面的第一组最小重复子串的fail值全是-1,后面就从0开始依次增加了。所以n-fail[n-1]-1就等于最小重复子串的长度。并且(n%min_n==0 && min_n!=n)时这个字符串才全是由最小重复子串组成的。

直接上代码

#include <iostream>
#include <string.h>
using namespace std;
const int MAX_N=1000000;
int biger=0;
int smaller=0;
int Equal=0;
int next[MAX_N];
int fail[MAX_N];
char str[MAX_N];
void Getnext(char* str){
    int i=0,p0,len=strlen(str);
    next[0]=len;
    while(str[i+1]==str[i] && i+i<len)
        ++i;
    next[1]=i;
    p0=1;
    for(int i=2;i<len;++i){
        if(next[i-p0]+i<next[p0]+p0)
            next[i]=next[i-p0];
        else{
            int j=next[p0]+p0-i;
            if(j<0) j=0;
            while(i+j<len && str[i+j]==str[j])
                ++j;
            next[i]=j;
            p0=i;
        }
    }
}

void Getfail(char* s) {
    int match=-1;
    fail[0]=-1;
    for(int i=1;s[i];++i){
        while(match >=0 && s[match+1]!=s[i]){
            match=fail[match];
        }

        if(s[match+1] == s[i]){
            match++;
        }

        fail[i]=match;
    }
}

int main() {
    scanf("%s",&str);
    Getnext(str);
    Getfail(str);
    int over=0;
    int len=strlen(str);
    int x=len-fail[len-1]-1;
    if(len%x == 0) over=len-x;
    for(int i=len-1;i>=over;i--){
        if(str[(i+next[i])%len]>str[next[i]%len])
            biger++;
        else if(str[(i+next[i])%len]<str[next[i]%len])
            smaller++;
        else
            Equal++;
    }

    if(over!=0)
    {
        int x=len/(smaller+biger+Equal);
        cout<<smaller*x<<" "<<biger*x<<" "<<Equal*x<<endl;
    }
    else
        cout<<smaller<<" "<<biger<<" "<<Equal<<endl;
    return 0;
}
### 数字旋转效果的实现方法 数字钟的旋转效果可以通过硬件和软件两种方式来实现。以下分别介绍这两种方法的具体实现。 #### 硬件实现 在硬件层面,数字钟的旋转效果通常通过调整数码管的显示角度或使用可旋转的显示屏来实现。例如,在基于单片机(如 AT89C2051)的数字钟设计中,可以通过机械结构设计使整个数码管模块旋转[^1]。此外,还可以使用带有旋转功能的液晶屏或其他显示设备替代传统的数码管,从而实现更灵活的显示效果。 另一种硬件实现方式是利用多块数码管组合,并通过控制每块数码管的角度和亮度,模拟出旋转的效果。这种方式需要额外的驱动电路和控制逻辑来协调多个数码管的工作状态。 #### 软件实现 在软件层面,数字钟的旋转效果主要依赖于图形处理技术。以下是一些常见的实现方法: 1. **CSS3 变换** 使用 CSS3 的 `transform` 属性可以轻松实现数字钟的旋转效果。例如,通过设置 `transform: rotate(45deg);`,可以使数字钟以指定的角度旋转。代码示例如下: ```css .digital-clock { transform: rotate(45deg); display: inline-block; font-size: 2em; font-family: monospace; } ``` 2. **JavaScript 动画** 如果需要动态控制旋转角度,可以结合 JavaScript 和 CSS 实现。例如,使用 `setInterval` 函数每秒更新一次旋转角度: ```javascript let angle = 0; const clockElement = document.querySelector('.digital-clock'); setInterval(() => { angle += 1; // 每次增加1度 clockElement.style.transform = `rotate(${angle}deg)`; }, 50); // 每50毫秒更新一次 ``` 3. **FPGA 图形处理** 在 FPGA 实现的数字钟中,可以通过编程生成具有旋转效果的时间显示。具体来说,可以将时间信息转换为二进制格式后,再通过硬件描述语言(如 Verilog 或 VHDL)设计一个图形旋转模块,该模块负责对显示内容进行旋转处理[^2]。 4. **jQuery 插件** 利用 jQuery 插件也可以实现数字钟的旋转效果。例如,结合 jQuery 和 CSS3 的变换功能,可以创建一个交互式的旋转数字钟。以下是一个简单的示例: ```javascript $(document).ready(function () { let angle = 0; setInterval(function () { angle += 2; // 每次增加2度 $('.digital-clock').css('transform', `rotate(${angle}deg)`); }, 100); // 每100毫秒更新一次 }); ``` #### 结合硬件与软件的综合实现 对于某些复杂的应用场景,可能需要同时结合硬件和软件来实现数字钟的旋转效果。例如,在 FPGA 设计中,可以使用硬件逻辑生成时间数据,并通过软件控制其显示角度和位置[^2]。这种方式能够提供更高的灵活性和性能。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值