题目
336. 回文对
我的思路
我的思路:
两两组合,再依次检查是否组合后的串是否是回文串。
检查回文串的方法:两个指针分别指向首和尾巴,若字符匹配则向中间靠近,直到不匹配或者靠拢
很明显,我的做法复杂度过高,运行超时了,如何优化?
我的做法的复杂度是,n*n*m,n是单词总数,m是字符串的平均长度。
个人感觉复杂度不理想的关键原因在对每个单词都要挨个尝试与其它所有单词的查询是否能组成回文对。
能不能一次性查多个呢?似乎是可以的,对于某个串s1,与它匹配乘回文对的串s2满足:
s1是s2的逆序串从头开始的子串,并且非子串部分是回文串
s2的逆序串是s1从头开始的子串,并且非子串部分是回文串
那么是不是找出每一个串满足后缀是回文的子串,存储到一个查找结构中。再遍历所有的串,对于每一个串找出前缀是回文的所有后缀子串,在查找结构中查找该子串的逆序串。若存在那么对应的就是可以形成匹配的回文对。
或者把所有串直接存储到一个存储结构中。再遍历所有的串,对每一个串,找出其前缀或后缀是回文的剩下的子串,在存储结构找查找子串的逆序即可,若存在那么对应的就是可以形成匹配的回文对。
我来尝试使用字典树来解决。
字典树的典型结构
vector<Node> DTree;//数组存储字典树 struct Node{ int ch[26];//存储的是该节点对应孩子字符的节点在数组中的下标 int flag;//既是终点标记,也是标记该串在原串表中的位置 Node() { flag = -1; memset(ch,0,sizeof(ch)); } } void insertWord(string str,int id) { int length = str.size(); int pos = 0; for(int i=0;i<length;i++) { int charInt = str[i] - 'a'; if(DTree[pos].ch[charInt]==0) { DTree.emplace_back();//不加参数的会调用默认构造函数 DTree[pos].ch[charInt] = DTree.size()-1; } pos = DTree[pos].ch[charInt]; } DTree[pos].flag = id; } int findWord(string str,int left,int right) { int pos = 0; int charInt; while(right>=left) { charInt = str[right] - 'a'; if(DTree[pos].ch[charInt]!=0) { pos = DTree[pos].ch[charInt]; } else { return -1; } right--; } return DTree[pos].flag; }
1.先生成一颗字典树
2.遍历所有串:
对每一个串,找所有前缀回文和后缀回文。在字典树中搜索,剩下的子串的逆序
我的实现
class Solution { public: struct Node{ int ch[26];//存储的是该节点对应孩子字符的节点在数组中的下标 int flag;//既是终点标记,也是标记该串在原串表中的位置 Node() { flag = -1; memset(ch,0,sizeof(ch)); } }; vector<struct Node> DTree;//数组存储字典树 void insertWord(string str,int id) { int length = str.size(); int pos = 0; for(int i=0;i<length;i++) { int charInt = str[i] - 'a'; if(DTree[pos].ch[charInt]==0) { DTree.emplace_back();//不加参数的会调用默认构造函数 DTree[pos].ch[charInt] = DTree.size()-1; } pos = DTree[pos].ch[charInt]; } DTree[pos].flag = id; } int findWord(string str,int left,int right) { int pos = 0; int charInt; while(right>=left) { charInt = str[right] - 'a'; if(DTree[pos].ch[charInt]!=0) { pos = DTree[pos].ch[charInt]; } else { return -1; } right--; } return DTree[pos].flag; } bool checkSymetric(string str,int left,int right) { while(left<right) { if(str[left]!=str[right]) { return false; } ++left; --right; } return true; } vector<vector<int>> palindromePairs(vector<string>& words) { struct Node temp; DTree.push_back(temp); int id=0; for(auto it:words) { insertWord(it,id++); } id = 0; vector<vector<int>> result; for(auto it:words) { int mirror = findWord(it,0,it.size()-1); if(mirror!=-1&&mirror!=id) {vector<int> t = {id,mirror};result.push_back(t);} int preLength=1; for(preLength;preLength<=it.size();preLength++) { if(checkSymetric(it,0,preLength-1)) { mirror = findWord(it,preLength,it.size()-1); if(mirror!=-1) {vector<int> t = {mirror,id};result.push_back(t);} } } int postLength=1; for(postLength;postLength<=it.size();postLength++) { if(checkSymetric(it,it.size()-postLength,it.size()-1)) { mirror = findWord(it,0,it.size()-postLength-1); if(mirror!=-1) {vector<int> t = {id,mirror};result.push_back(t);} } } id++;} return result; } };
拓展学习
树的结构:用多维数组表示,大数组的每一项代表一个节点,且每个节点存有存有字符集数量(比如26)个元素以及当前是否是终结节点。
插入、查询
要熟练