package main import ( "bufio" "fmt" "os" ) func main() { yuki263() } // No.263 Common Palindromes Extra // 求两个字符串的公共回文子串的个数 n<=5e5 // https://yukicoder.me/problems/no/263 func yuki263() { in := bufio.NewReader(os.Stdin) out := bufio.NewWriter(os.Stdout) defer out.Flush() var s, t string fmt.Fscan(in, &s, &t) T := NewPam() T.AddString(s + "><" + t) dps := make([]int, T.Size()) dpt := make([]int, T.Size()) lenS := int32(len(s)) for i := 0; i < T.Size(); i++ { for _, j := range T.Nodes[i].Indexes { // 回文出现位置 if j < lenS { dps[i]++ } else if j >= lenS+2 { dpt[i]++ } } } res := 0 for i := T.Size() - 1; i >= 2; i-- { // 按照拓扑序遍历本质不同回文 res += dps[i] * dpt[i] dps[T.Nodes[i].Link] += dps[i] dpt[T.Nodes[i].Link] += dpt[i] } fmt.Fprintln(out, res) } type Node struct { Next map[int32]int32 // 子节点. Link int32 // suffix link,指向当前回文串的最长真回文后缀的位置 Length int32 // 结点代表的回文串的长度 Indexes []int32 // 哪些位置的最长回文后缀 } type Pam struct { Ords []int32 Nodes []*Node lastPos int32 // 当前字符串(原串前缀)的最长回文后缀 } func NewPam() *Pam { res := &Pam{} res.Nodes = append(res.Nodes, res.newNode(0, -1)) // 奇根,长为 -1 res.Nodes = append(res.Nodes, res.newNode(0, 0)) // 偶根,长为 0 return res } func (pt *Pam) Add(x int32) int { pos := int32(len(pt.Ords)) pt.Ords = append(pt.Ords, x) cur := pt.findPrevPalindrome(pt.lastPos) _, hasKey := pt.Nodes[cur].Next[x] if !hasKey { pt.Nodes[cur].Next[x] = int32(len(pt.Nodes)) } pt.lastPos = pt.Nodes[cur].Next[x] if !hasKey { pt.Nodes = append(pt.Nodes, pt.newNode(-1, pt.Nodes[cur].Length+2)) if pt.Nodes[len(pt.Nodes)-1].Length == 1 { pt.Nodes[len(pt.Nodes)-1].Link = 1 } else { pt.Nodes[len(pt.Nodes)-1].Link = pt.Nodes[pt.findPrevPalindrome(pt.Nodes[cur].Link)].Next[x] } } pt.Nodes[pt.lastPos].Indexes = append(pt.Nodes[pt.lastPos].Indexes, pos) return int(pt.lastPos) } func (pt *Pam) AddString(s string) { if len(s) == 0 { return } for _, v := range s { pt.Add(v) } } func (pt *Pam) Size() int { return len(pt.Nodes) } func (pt *Pam) GetNode(pos int) *Node { return pt.Nodes[pos] } func (pt *Pam) newNode(link, length int32) *Node { return &Node{ Next: make(map[int32]int32), Link: link, Length: length, } } func (pt *Pam) findPrevPalindrome(cur int32) int32 { pos := int32(len(pt.Ords) - 1) for { rev := pos - 1 - pt.Nodes[cur].Length // !插入当前字符的条件str[i]==str[i-len-1] if rev >= 0 && pt.Ords[rev] == pt.Ords[len(pt.Ords)-1] { break } cur = pt.Nodes[cur].Link } return cur }