# 密码校验程序

# 描述

密码要求:

  1. 长度超过8位
  2. 包括:大写字母/小写字母/数字/其它符号,以上四种至少三种
  3. 不能分割出两个相等的长度大于 2 的子串,例如 abcabc 可以分割出两个 abc,不合法,ababa 则无法分割出2个aba。

注:其他符号不含空格或换行

数据范围:输入的字符串长度满足 1 ≤ n ≤ 100

# 输入描述:

一组字符串。

# 输出描述:

如果符合要求输出:OK,否则输出NG

# 示例

输入:
    021Abc9000
    021Abc9Abc1
    021ABC9000
    021$bc9000
    021Abc1111

输出:
    OK
    NG
    NG
    OK
    OK

# 解题思路

  1. 长度判断
  2. 字符种类判断
  3. 子串判断
package main

import (
	"bufio"
	"fmt"
	"os"
	"unicode"
)

// 密码长度校验 密码长度超过8位 校验通过
func passwordLengthCheck(password string) bool {
	if len(password) <= 8 {
		return false
	}
	return true
}

// 密码字符种类校验 密码包括:大写字母/小写字母/数字/其它符号,以上四种至少三种 校验通过
func passwordContainCheck(password string) bool {
	hasLower, hasUpper, hasDigit, hasSpecial := false, false, false, false
	for _, char := range password {
		if unicode.IsLower(char) {
			hasLower = true
		} else if unicode.IsUpper(char) {
			hasUpper = true
		} else if unicode.IsDigit(char) {
			hasDigit = true
		} else if char != ' ' && char != '\n' {
			hasSpecial = true
		}
	}
	types := 0
	if hasLower {
		types++
	}
	if hasUpper {
		types++
	}
	if hasDigit {
		types++
	}
	if hasSpecial {
		types++
	}
	// 校验
	if types >= 3 {
		return true
	}
	return false
}

// 密码子串校验 不能分割出两个相等的长度大于 2 的子串 校验通过
func passwordRepeatCheck(password string) bool {
	rm := make(map[string]struct{})
    length := len(password)
	for i := 3; i < length/2; i++ {
		for j := 0; j < length-i; j++ {
			subStr := password[j : j+i]
            if _, ok := rm[subStr]; ok {
                return false
            }
            rm[subStr] = struct{}{}
		}
	}
	return true
}

// 主函数
func main() {
	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		password := scanner.Text()
		// 长度校验
		if passwordLengthCheck(password) && passwordContainCheck(password) && passwordRepeatCheck(password) {
			fmt.Println("OK")
		} else {
			fmt.Println("NG")
		}
	}
}

# 技巧

  1. 使用bufio 包来读取输入。
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
    password := scanner.Text()
}
  1. 使用 unicode 包来判断字符类型
for _, char := range password {
    if unicode.IsLower(char) {
        hasLower = true
    } else if unicode.IsUpper(char) {
        hasUpper = true
    } else if unicode.IsDigit(char) {
        hasDigit = true
    } else if char != ' ' && char != '\n' {
        hasSpecial = true
    }
}
  1. 使用 map[string]struct{} 来存储子串,struct{} 是一个空结构体,不占用内存空间。
rm := make(map[string]struct{})
  1. 最大的重叠子串长度为字符串长度的一半。减少外层循环,减少内存占用。
for i := 3; i < length/2; i++ {
    
}
  1. 内部循环,从 0 开始,每次增加 i,起始位置为 j,结束位置为 j+i。j+i 超过字符串长度时,跳出循环。
for j := 0; j < length-i; j++ {
    subStr := password[j : j+i]
}
  1. map底层是哈希结构,我们直接判断子串是否存在,避免哈希写入和读取的开销。
if _, ok := rm[subStr]; ok {
    return false
}