2016.5.11_经典试题-回文算法【ABAP】
回文:在一串字符中找到对称出现的字符。
例:7 19 56 99 99 56 24 7 29 1 中回文为:56 99
题目要求输入字符串,找出回文。
思路:
将字符串split进一个内表1,loop表1时创建内表2,两个表对比找一样的字符内容。
内容一致时表1向下一位寻找,表2向上一位寻找,在同一个字符串中找两个位置。
找到则继续,找不到则跳出。
DATA GT_DATA TYPE STANDARD TABLE OF STRING WITH HEADER LINE. DATA GT_COPY TYPE STANDARD TABLE OF STRING WITH HEADER LINE. DATA GS_DATA LIKE LINE OF GT_DATA . DATA GV_ANSWER TYPE STRING. DATA GV_COUNT TYPE I. PARAMETER:P_INPUT TYPE STRING DEFAULT '7 19 56 99 99 56 24 7 29 1'. START-OF-SELECTION. SPLIT P_INPUT AT SPACE INTO TABLE GT_DATA. DATA LV_LINES TYPE I. DATA LV_REST TYPE I. DATA LV_COUNT TYPE I. DATA LV_UP LIKE SY-TABIX. DATA LV_DN LIKE SY-TABIX. DATA LV_ANSWER TYPE STRING. DATA LV_DATA TYPE STRING. LV_LINES = LINES( GT_DATA ). LOOP AT GT_DATA. LV_UP = SY-TABIX. LV_REST = LV_LINES - LV_UP. GT_COPY[] = GT_DATA[]. DELETE GT_COPY FROM 1 TO LV_UP. LOOP AT GT_COPY WHERE TABLE_line EQ GT_DATA. LV_DN = SY-TABIX + LV_UP. DO LV_REST TIMES. IF LV_DN LE LV_UP. CLEAR:LV_COUNT,LV_ANSWER. EXIT. ENDIF. READ TABLE GT_DATA INDEX LV_UP. READ TABLE GT_DATA INTO GS_DATA INDEX LV_DN. IF GT_DATA EQ GS_DATA. LV_COUNT = LV_COUNT + 1. LV_DATA = GS_DATA. CONCATENATE LV_ANSWER LV_DATA INTO LV_ANSWER SEPARATED BY SPACE. IF LV_COUNT GT GV_COUNT. GV_COUNT = LV_COUNT. GV_ANSWER = LV_ANSWER. ENDIF. LV_UP = LV_UP + 1. LV_DN = LV_DN - 1. ELSE. LV_DN = LV_DN - 1. CLEAR:LV_COUNT,LV_ANSWER. EXIT. ENDIF. ENDDO. ENDLOOP. ENDLOOP. WRITE: / GV_COUNT, / GV_ANSWER.
这个题算法目前有点难以理解。希望半年后再来看这个题能够完全看懂。2016年11月11号再做一遍这道题。
----------------------20160622更新-------------------------------------------------
又遇见一道类似的题,要求找出字符串中最长回文。领导讲解了另一个类型的解法,
因为上面的输入之间有空格,可以方便的split到表里,这道题的输入是一个整体的字符串,
中间没有空格,所以领导用的是角标/位置的方法挨个找,处理对象是整个字符串(上面那种对象是内表)。
处理逻辑用到了递归,感觉略复杂,看代码看了两天还是觉得看不太明白....
整理思路如下:
假设字符串gv_string长度为20,那么第一位的角标为0,最后一位的角标为20-1=19.
这样的话第一位值为gv_string+0(1);最后一位为gv_string+19(1).
在循环do里每次从前加角标数,嵌套do里从后减角标数,就实现了在整个字符串里进行前后挨个对比。
但是寻找回文的过程有很多条件会使回文不成立,例如:
前位角标pos1大于后位角标pos2(不成立):
假设前位角标为5,说明前四位已经都和字符串剩下的部分对比找过回文了,这时候后位角标应该从19往前找,
找到5就要跳出里面的循环do了,再往前找4位是没有意义的。所以应该满足pos1 lt pos2.
回文长度大于整个字符串的一半(不成立):
整个字符串长度为20,一半为10,当查找回文的单个字符个数大于等于10时,说明回文长度(在目前的嵌套do里)已经是最大了。
计数是计算查找回文找到的次数(这段代码都写在下面那个form里,递归调用),就是计算递归这个form连续用了多少次,
能走到计数的那一步说明能找到回文。长度最大就退出;长度还不够大就让前位向后、后位向前继续寻找相同回文。
在外面的嵌套do上,也有很多限制条件:
外面的do是从前往后挨个过滤字符,里面的do是从后往前挨个过滤字符。
在里面的do循环时pos1是不变的,类似这样:
字符串:ABCDEFG
A->G , A->F , A->E , A->D , A->C , A->B (pos1就是A的角标)
然后外面的do第二次:
B->G , B->F , B->E , B->D , B->C
...
因为题目说明A还有AA这种小于三位的不算回文,所以在嵌套do前做了限制,
让外面的do的次数等于字符串长度-2:
(字符串ABCDEFG,当外面的do找到F这个字符时,里面的do从后往前只会找最后一位G)
字符串外面do找回文找到倒数第2位,那么就算找到了就是AA这种形式,可以排除这种情况,
-2这种设置为了减少循环次数;从前往后找避免了这种情况,同理在里面的do也做了这种排除:
(这里不太理解)
全部代码:
DATA GT_SPLIT TYPE TABLE OF STRING . DATA: GV_CNT TYPE I, GV_ANSWER TYPE STRING. DATA GS_IN TYPE STRING. PARAMETERS P_INPUT TYPE STRING DEFAULT '34 AVFSIOAAOIDAGEPLARFEGRTHTRGEFWXGGH'. START-OF-SELECTION. GS_IN = P_INPUT. PERFORM F_GET_LENGTH USING GS_IN CHANGING GV_ANSWER. IF GV_ANSWER IS INITIAL. GV_ANSWER = 0. ENDIF. WRITE GV_ANSWER. *&---------------------------------------------------------------------* *& Form F_GET_LENGTH *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* * -->PV_IN text * -->PV_ANSWER text *----------------------------------------------------------------------* FORM F_GET_LENGTH USING PV_IN CHANGING PV_ANSWER. DATA : LV_LEN_C(10) TYPE C, LV_DATA(1000) TYPE C, LV_LEN TYPE I, LV_POS1 TYPE I, LV_POS2 TYPE I, LV_ANSWER TYPE I, LV_TMS1 TYPE I, LV_TMS2 TYPE I, LV_ASW_LEN TYPE I. SPLIT PV_IN AT SPACE INTO LV_LEN_C LV_DATA. LV_LEN = LV_LEN_C. IF LV_LEN < 3. PV_ANSWER = 0. EXIT. ENDIF. LV_TMS1 = LV_LEN - 2. LV_TMS2 = LV_LEN. DO LV_TMS1 TIMES. LV_ASW_LEN = LV_TMS2. LV_POS2 = LV_LEN - 1. DO LV_TMS2 TIMES. CLEAR GV_CNT. PERFORM F_GET_ANW USING LV_POS1 LV_POS2 LV_DATA LV_ASW_LEN CHANGING LV_ANSWER. IF LV_ANSWER > PV_ANSWER. PV_ANSWER = LV_ANSWER. ENDIF. LV_ASW_LEN = LV_ASW_LEN - 1. IF LV_ASW_LEN < 3. EXIT. ENDIF. LV_POS2 = LV_POS2 - 1. ENDDO. LV_TMS2 = LV_TMS2 - 1. LV_POS1 = LV_POS1 + 1. ENDDO. ENDFORM. "F_GET_LENGTH *&---------------------------------------------------------------------* *& Form F_GET_ANW *&---------------------------------------------------------------------* * text *----------------------------------------------------------------------* * -->PV_POS1 text * -->PV_POS2 text * -->PV_DATA text * <--PV_ANSWER text *----------------------------------------------------------------------* FORM F_GET_ANW USING PV_POS1 PV_POS2 PV_DATA PV_LEN CHANGING PV_ANSWER. DATA : LV_VAL1 TYPE C, LV_VAL2 TYPE C, LV_DIV TYPE I, LV_POS1 TYPE I, LV_POS2 TYPE I. CLEAR : LV_VAL1, LV_VAL2, PV_ANSWER. IF PV_POS1 >= PV_POS2. EXIT. ENDIF. LV_VAL1 = PV_DATA+PV_POS1(1). LV_VAL2 = PV_DATA+PV_POS2(1). IF LV_VAL1 NE LV_VAL2. EXIT. ENDIF. LV_DIV = PV_LEN DIV 2. GV_CNT = GV_CNT + 1. IF GV_CNT >= LV_DIV. PV_ANSWER = PV_LEN. EXIT. ELSE. LV_POS1 = PV_POS1 + 1. LV_POS2 = PV_POS2 - 1. PERFORM F_GET_ANW USING LV_POS1 LV_POS2 PV_DATA PV_LEN CHANGING PV_ANSWER. ENDIF. ENDFORM. "F_GET_ANW
两种解法只是换了对象,思路是一样的,对一个字符串一边从前往后,一边从后往前去挨个遍历,就是退出循环的条件比较抽象,
还需要多看看,争取能自己写出来。今年要啃下这道题!