isaacpei / algorithm-interview

Weekly algorithm questions for interview
18 stars 0 forks source link

Q001_大菠萝_solution #2

Open biganans opened 6 years ago

biganans commented 6 years ago

Question_ID: Q001

Language: Lua 5.3.2

Time Cost: 90-mins

Time Complexity: {}

Solution

  1. 设置四个符号检测:"<"、">"、"</"、"/>"
  2. 把所给的xml语句分离出三种数据:标签数据、标签内容数据、特殊标签("/>")数据并记录每个数据在第几层,根据标签数据记录start_kh来定的当前层级
  3. 组装三种输入答案

My Code

print("please input xml:")
local readStr = io.read()
-- local readStr = "<a>123<b>456</b></a>"
-- local readStr = "<a>123<b>456<c/></b></a>"
print("U put:"..readStr)

print("ok,let's go")

local start_kh1 = "<"
local start_kh2 = ">"
local start_kh3 = "</"
local start_kh4 = "/>"

--记录前面ok的格式:<a> 
local start_kh={}
--记录后面ok的格式:</a>
-- local end_kh = {}
--合法的格式:<a/> 直接跳过
--记录对应的层级关系数据
local level_date = {}
--记录start_kh4数据回车换行使用
local spe_data = {}

local spos = 0
local level = 0
local content = ""
local find = false
local kh3ok = false
-- for i=1,#readStr do
local i = 1
while(1) do
    if i > #readStr then break end
    local tmp = string.sub(readStr,i,i)
    -- print(tmp)
    if tmp == start_kh1 then
        if #content > 0 then
            level_date[level] = content
            content = ""
        end
        level = level+1
        kh3ok = false
        spos = i
    elseif tmp == "/" then
        if spos > 0 then
            -- 表示可能属于start_kh4 或者 start_kh3
            if spos + 1 == i then
                -- start_kh3
                kh3ok = true
            else
                local end_str = string.sub(readStr,i+1,i+1)
                if end_str and tmp..end_str == start_kh4 then
                    --start_kh4 直接忽略哒 
                    content = start_kh1 .. content
                    content = content .. tmp ..end_str
                    i = i + 1
                    spos = 0
                    spe_data[level] = content
                    -- 否则再次遇到<就不对了
                    level = level - 1
                    content = ""
                else
                    --没有任何匹配
                    content = content .. tmp
                end
            end
        else
            -- igone
            content = content .. tmp
        end
    elseif tmp == start_kh2 then
        if #content > 0 then
            -- 是标签
            if spos > 0 and not kh3ok then
                start_kh[level] = content
                -- 这个时候需要查找前面是否有这个标签
                find = false
                for k,v in ipairs(start_kh) do
                    if v == content then
                        find = true
                        break
                    end
                end
                print(find)
                if not find then
                    print("error >>>>>>>> "..content)
                    return
                end
                content = ""
            else
                -- content = content..tmp
                content = ""
            end
            -- content = ""
        else
            content = content..tmp
        end
    elseif tmp then
        content = content..tmp
    end
    i = i + 1
end

local tab = "    "
local print_str1 = ""
local print_str2 = ""
for i=1,#start_kh do
    local bq1 = start_kh1..start_kh[i]..start_kh2
    local bq2 = start_kh3..start_kh[i]..start_kh2
    for j=2,i do
        bq1 = tab..bq1
        bq2 = tab..bq2
    end

    print_str1 = print_str1..bq1.."\n"
    bq1 = ""
    for j=1,i do
        bq1 = tab..bq1
    end
    print_str1 = print_str1..bq1..level_date[i].."\n"

    if spe_data[i+1] then
        print_str1 = print_str1 .. bq1 ..spe_data[i+1].."\n"
    end

    if #print_str2 > 0 then
        print_str2 = bq2.."\n"..print_str2
    else
        print_str2 = bq2
    end

end

print("result:")
print(print_str1..print_str2)

Other

lua的字符串数组直接用readStr[i]取出来是nil,刚开始怎么都不对后面查阅了一番。 写的比较烂,大家勿喷哈。

rbee3u commented 6 years ago

我觉得代码写得有点儿太长了,是不是应该组织一下逻辑想想怎么缩短

isaacpei commented 6 years ago

太长了, 简单的逻辑就要写简单一点, 这个题其实从读题到写完就期望20分钟而已, 不要搞这么复杂呀... 另外我看不太懂lua

rbee3u commented 6 years ago

我给写了个lua版的,你参考参考?

-- '<': 60, '>': 62, '/':47
function fmt(text)
    local char_at = function(i) -- 获取字符串元素
        return string.byte(text, i)
    end
    local acc = {} -- 结果存储表
    local ind = 0  -- 缩进空格数
    local i, j = 1, nil -- 一前一后两个游标
    local push = function() -- 结果表添加元素
        table.insert(acc, string.rep(" ", ind)
                    .. string.sub(text, i, j))
    end
    local match_tag = function() -- 匹配标签(j 最终指向 '>')
        while char_at(j + 0) ~= 62 do j = j + 1 end
    end
    local match_ctt = function() -- 匹配内容(j 最终指向 '<' 的前一个位置)
        while char_at(j + 1) ~= 60 do j = j + 1 end
    end
    while char_at(i) ~= nil do
        if char_at(i) == 60 then -- i 指向 '<' 则开始匹配标签
            j = i + 1; match_tag()
            if     char_at(i + 1) == 47 then -- 如果标签以 '</' 开始
                ind = ind - 4; push() -- 表示结束标签,先减少缩进
            elseif char_at(j - 1) ~= 47 then -- 如果标签不以 '/>' 结束
                push(); ind = ind + 4 -- 表示开始标签,下一行增加缩进
            else
                push() 
            end
        else -- i 不指向 '<' 则开始匹配内容
            j = i + 0; match_ctt()
            push()
        end
        i = j + 1
    end
    return table.concat(acc, "\n")
end

print(fmt("<a>123<b>456<c/></b></a>"))