ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 9976|回复: 34

[Excel 程序开发] [第99期][VBA&函数公式]最少的旋转次数[已总结]

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2013-10-11 10:53 | 显示全部楼层 |阅读模式
本帖最后由 delete_007 于 2013-11-27 16:28 编辑

VBA 题的解题思路和参考答案见 13楼,评分总结见 28楼
函数总结评分见33楼

题目内容:
小明和人玩转盘游戏,转盘外圈写有数个字母,转盘外12点钟方向的固定位置作为起始点,游戏规则是每次顺时针转动一个字母的位置,由起始点开始顺时针计由各字母组成的字符串,当出现最小字符串时游戏结束,先到者赢。

我们的工作是帮小明把转动次数算出来,这样就能保赢了。

为此我们设置一个字符串,由起始点顺时针计转盘的各字母,每转动一次代表将该字符串左侧的第一个字母移动到右侧末尾,比如 "excel" 转动一次就成了 "xcele"。现在要计算到达最小字符串的最小转动次数。

例如,"excel" -> 2,"mississippi" -> 10

以mississippi为例:
[code=vb]转动次数,字符串
00,     mississippi
01,     ississippim
02,     ssissippimi
03,     sissippimis
04,     issippimiss
05,     ssippimissi
06,     sippimissis
07,     ippimississ
08,     ppimississi
09,     pimississip
10,     imississipp <-在所有可能出现的字符串中,此字符串最小
11,     mississippi <-回到初始顺序[/code]

答题要求:
题一(VBA题)、编写如下自定义函数:MinRot,并在B2单元格录入公式”=MinRot(A2)"下拉完成。
[code=vb]Public Function MinRot(ByVal sStr As String) As Long
    Dim t#
    t = Timer
    '...
    t = Timer - t
    Debug.Print "ID: " & "<Your ID>"
    Debug.Print "Answer is: " & MinRot
    Debug.Print "Time used: " & Format(t, "0.000s")
End Function[/code]
输入的字符串由小写英文字母‘a’~‘z’构成,长度不超过100,000个字符。

题二(函数题)、纯函数题,不允许其他任何操作,C2录入公式下拉完成。(字符串由小写英文字母构成,长度小于100字符。

评分规则:
题一、
1、能正确计算,且对26字母随机组成的字符串能在0.5秒内完成,得1分
2、能对任何符合题目要求的字符串在0.1秒内完成,另得1分
执行速度是以评分人(也就是楼主)的机器为准

题二、
公式长度(含等号)小于120字符评1分

提示:
仔细审题,注意特殊的字符串。

比赛日期:2013-10-26至2013-11-25,在此之前请勿答题。
请勿多占楼层,否则取消参赛资格。
如出现无法编辑自己帖子的情况,请与delete_007联系。













补充内容 (2013-10-18 15:37):
回答VBA题,请直接贴代码,不要提交附件,不要更改函数的定义行

最小字符串:
等同于最小字典顺序(字典中出现在前的有较小的顺序)
或直接比较两字符串的大小(Debug.Print StrA < StrB)


楼主机器 1 秒对比:
Sub z()
Dim t#, i&, s$
t = Timer
For i = 1 To 69000
s = s & Chr(Int(Rnd * 26 + 97))
Next
Debug.Print Timer - t
End Sub


VBA题,第 2 项评分规则放宽到 0.5 秒,上述时间测试代码改为48000为楼主机器需时约 0.5 秒。

特殊字符串:
不仅是同一字符,还会有按一些特殊顺序排列的几个字符,比如zyzyyzyyyz...
真心希望各位愉快拿分


请答VBA题的仅保留唯一代码,否则只评测帖子中的最后一个。附件将不被考虑。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?免费注册

x

评分

3

查看全部评分

头像被屏蔽

TA的精华主题

TA的得分主题

发表于 2013-10-16 10:15 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2013-11-25 18:46 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
今天先做了100期的总结,这期明天中午找时间做,这期函数比较简单,基本属于送分,所以总结难度也不大。

TA的精华主题

TA的得分主题

发表于 2013-10-26 17:07 | 显示全部楼层
本帖最后由 yangyangzhifeng 于 2013-10-26 18:09 编辑

写了一个自定义函数
  1. Function MinRot(ByVal sstr As String) As Long    Dim t#
  2.     t = Timer
  3.     Dim i&, x&, ck As Boolean, j&, y&, z&, n&
  4.     n = Len(sstr)
  5.     ReDim jg&(1 To n)
  6.     For i = 1 To n
  7.         jg(i) = Asc(Mid(sstr, i, 1))
  8.     Next
  9.     z = 1
  10.     For i = 2 To UBound(jg)
  11.         x = i: j = 0: y = z
  12.         Do
  13.             If jg(x) > jg(y) Then Exit Do
  14.             If jg(x) < jg(y) Then
  15.                 ck = True
  16.                 Exit Do
  17.             End If
  18.             j = j + 1
  19.             x = x + 1
  20.             If x > n Then x = 1
  21.             y = y + 1
  22.             If y > n Then y = 1
  23.             If j = n Then Exit Do
  24.         Loop
  25.         If ck Then MinRot = i - 1: z = i: ck = False
  26.     Next
  27.     t = Timer - t
  28.     Debug.Print "ID: " & "yangyangzhifeng"
  29.     Debug.Print "Answer is: " & MinRot
  30.     Debug.Print "Time used: " & Format(t, "0.000s")
  31. End Function
复制代码

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-10-26 22:34 | 显示全部楼层
不是很明白题意。

=IF(LEFT(A2)=RIGHT(A2),1,MATCH(1=1,MID(A2&A2,ROW($2:$99),1)=MID(A2&A2,LEN(A2)+1,1),)-1)
数组公式。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?免费注册

x

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-10-27 17:08 | 显示全部楼层
不知道什么叫“特殊字符串”,没法考虑。
凑个数吧:
[code=vb]
Function MinRot(ByVal sStr As String) As Long
    Dim t#
    t = Timer
    Dim tm$, s$, i&, n&
    tm = sStr: s = sStr
    For i = 1 To Len(sStr) - 1
        s = Mid(s, 2) & Left(s, 1)
        If tm > s Then
            n = i
            tm = s
        End If
    Next i
    MinRot = n
    t = Timer - t
    Debug.Print "ID: " & "小花鹿"
    Debug.Print "Answer is: " & MinRot
    Debug.Print "Time used: " & Format(t, "0.000s")
End Function

[/code]

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-10-27 18:30 | 显示全部楼层
本帖最后由 香川群子 于 2013-11-1 12:39 编辑

时间测试代码运行结果:
             上班使用老电脑     家里老电脑
             1.60GHz 512MB
100,000   5.316406
69,000     2.742188 秒        1.687500 秒
48,000    1.429688 秒        0.953125 秒

呵呵,我的机器弱爆了……但我的代码好啊,跑起来照样很快。

11/01修改,按竞赛要求的函数是:


  1. Function MinRot(ByVal sStr As String) As Long
  2.     Dim t#, i&, j&, k&, l&, m&, n&, r&, ii&, jj&, t0$, t1$, t2$, tt$, sStr2$
  3.     t = Timer
  4.    
  5.     For i = 1 To 26
  6.         t0 = Chr(i + 96)
  7.         j = InStr(sStr, t0): If j Then m = j: n = i: Exit For '找到最先出现的最小字符位置
  8.     Next
  9.     l = Len(sStr)
  10.     If String(l, t0) = sStr Then
  11.         MinRot = 0 '如字符串由同一最小字符组成则结果=0,直接退出 (10/31修改)
  12.     Else
  13.         sStr2 = sStr & sStr '待检字符
  14.         r = l '最小字符的最大重复值初始化
  15.         For ii = n + 1 To 26 '遍历检查其它字符
  16.             jj = InStr(j, sStr2, Chr(ii + 96))
  17.             If jj Then If jj - j < r Then r = jj - j '找到最小的即第一个其它字符位置
  18.         Next
  19.         t1 = Mid(sStr2, m, r)
  20.         '以上为检查获取最小字符的最大重复值(11/01新添加)
  21.    
  22.         For i = r + 1 To l '遍历检查
  23.             j = m: k = 1: t2 = Mid(sStr2, j, i) '每次增加1个字符来检查
  24.             Do
  25.                 j = InStr(j + i, sStr2, t1)
  26.                 '找到下一个前面相同的字符位置,需排除以截取字符位置改为j+i (10/31修改)
  27.                 If j > l Or j = 0 Then Exit Do '如已到底或不含最小字符则结束(10/31修改)
  28.             
  29.                 tt = Mid(sStr2, j, i) '否则截取相同个数i个字符
  30.                 If tt < t2 Then '如字符值更小
  31.                     If String(i, t0) = tt Then '如全是最小字符(11/01新添加)
  32.                         r = l '最小字符的最大重复值初始化
  33.                         For ii = n + 1 To 26
  34.                             jj = InStr(j, sStr2, Chr(ii + 96))
  35.                             If jj Then If jj - j < r Then r = jj - j  '找到最小的即第一个其它字符位置
  36.                         Next
  37.                         If r > i Then i = r: tt = Mid(sStr2, j, i)
  38.                          '如最小字符最大值r比当前i值更大则取最大值
  39.                     End If
  40.                     '以上新增以快速确定新位置开始是否有更长的相同最小字符(11/01新添加)
  41.                
  42.                     t2 = tt: k = 1: m = j '则更新记录并初始化
  43.                 ElseIf tt = t2 Then '如相同
  44.                     k = k + 1 '则记录相同字符出现次数k
  45.                 End If
  46.             Loop
  47.             If k = 1 Then Exit For Else t1 = t2 '如相同字符只有一个就可退出,否则以当前最小字符作为新的检查字符
  48.         Next
  49.         MinRot = m - 1 '减去1得到相对位置(当前位置=0)
  50.     End If
  51.     Debug.Print Space(10)
  52.     Debug.Print "MinRot Code is: " & IIf(tt = "", t1, t2) '输出最小字符
  53.     Debug.Print "MinRot Long is: " & IIf(tt = "", i - 1, i) '输出最小字符长度
  54.     '以上增加输出结果资料,是否违规? 如违规请告知,或直接注释掉

  55.     t = Timer - t
  56.     Debug.Print "ID: " & "<kagawa>"
  57.     Debug.Print "Answer is: " & MinRot
  58.     Debug.Print "Time used: " & Format(t, "0.000s")

  59.     Debug.Print Space(10)   
  60. End Function
复制代码

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-10-28 07:57 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 yuanhongly 于 2013-10-31 15:57 编辑

瞎胡搞,没套路,还好计算结果是对的   

(我的电脑运行 1万位字符内 0.000s    5万字符0.008s   10万位字符内0.016s  50万位字符 0.094s )

[code=vb]
Function MinRot(ByVal sSTR As String)
    Dim t#
    t = Timer
    Dim FD, i, UI, UI2, FindStr$, newFStr$, iStr$, iStr2$, istrLen, JGStr$, FMStr$
    iStr = sSTR + Left(sSTR, Len(sSTR) - 1)
    istrLen = Len(iStr)
    For i = asc("a") To asc("z")
        newFStr = FindStr + Chr(i)
        UI = InStr(iStr, newFStr)
        If UI > 0 Then
                FD = Split(iStr, newFStr)
                x = UI
                FMStr = Mid(iStr, x + 1, istrLen - x)
                If UBound(FD) < 10 Or Len(newFStr) > 10 Then
                    Do While x > 0
                        x = InStr(x + Len(newFStr), iStr, newFStr)
                        If x = 0 Then Exit Do
                        tmpstr = Mid(iStr, x + 1, istrLen - x)
                            If FMStr > tmpstr And tmpstr <> "" And tmpstr > newFStr And Left(FMStr, Len(tmpstr)) <> tmpstr Then
                                FMStr = tmpstr
                            End If
                    Loop
                    JGStr = Left(newFStr, 1) + FMStr
                    MinRot = InStr(iStr, JGStr) - 1
                    Exit For
               End If
                    FindStr = newFStr
                    i = asc("a") - 1
        Else
                If Len(newFStr) > 1 Then
                    FindStr = Left(newFStr, Len(newFStr) - 1)
                Else
                    FindStr = ""
                End If
        End If
    Next
    t = Timer - t
    Debug.Print "        ID: " & "yuanhongly"
    Debug.Print "Answer  is: " & MinRot
    Debug.Print "Time  used: " & Format(t, "0.000s")
   'Debug.Print "Min string: " & Mid(iStr, MinRot + 1, Len(sSTR))

End Function[/code]

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-10-28 22:40 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
=SUBSTITUTE(FIND(CHAR(MATCH(1>0,LEN(SUBSTITUTE(A2,CHAR(ROW($97:$122)),))=(LEN(A2)-1),)+96),A2)-1,0,LEN(A2)-1)

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2013-10-29 21:51 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
Option Explicit

Function MinRot(ByVal sStr As String) As Long
    Dim t#
    t = Timer
    '……
Dim org(), d(), a()
Dim ls, sum, suma
Dim i, j, ttmp
Dim minx
ls = Len(sStr)
ReDim org(ls)
ReDim d(ls)

minx = 200
sum = 0
For i = 1 To ls
     ttmp = Asc(Mid(sStr, i, 1))
    org(i) = ttmp
    If minx = ttmp Then
        sum = sum + 1: d(sum) = i
    ElseIf ttmp < minx Then
        sum = 1: d(1) = i: minx = ttmp
    End If
Next

ReDim a(sum + 1)
j = 1: i = 1
Do
    suma = 0
    minx = 200
   
   For i = 1 To sum
         ttmp = org((d(i) + j) Mod ls)
         
         If minx = ttmp Then
            suma = suma + 1: a(suma) = d(i)
         ElseIf ttmp < minx Then
            suma = 1: a(1) = d(i): minx = ttmp
        End If
    Next
   
    j = j + 1:    sum = suma
    For i = 1 To suma
    d(i) = a(i)
    Next
   
    If suma = 1 Then Exit Do

Loop


MinRot = d(1) - 1
Debug.Print vbCrLf, Mid(sStr, d(1)) & Left(sStr, d(1) - 1)


    '==============
    t = Timer - t
    Debug.Print "ID: " & "Jimmy_314"
    Debug.Print "Answer is: " & MinRot
    Debug.Print "Time used: " & Format(t, "0.000s")
End Function
''' 0.512 seconds

评分

2

查看全部评分

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-21 19:02 , Processed in 0.052478 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表