├── .gitignore ├── string ├── KMP │ ├── figures │ │ ├── BF.png │ │ ├── prefix.png │ │ ├── x!=now.png │ │ ├── move2try.png │ │ ├── next_intro.png │ │ ├── ruler_move.png │ │ ├── quick_final.png │ │ ├── 1st_time_match.png │ │ ├── how2movebynext.png │ │ └── quick_solve_next.png │ ├── src │ │ ├── next_solve_init_demo.cpp │ │ ├── kmp_python_ver.py │ │ ├── kmp_java_ver.java │ │ ├── kmp_ref.cpp │ │ └── kmp_demo.cpp │ └── README.md └── toupper │ ├── src │ ├── c_001.c │ ├── c_002.c │ ├── c_003.c │ ├── main.c │ ├── makefile │ ├── c_004.c │ ├── c_005.c │ └── c_006.c │ └── README.md ├── README.md ├── graph theory └── DFS │ ├── figures │ └── startidx.png │ ├── src │ ├── 78. Subsets.cpp │ ├── 77. Combinations.cpp │ ├── 46. Permutations.cpp │ ├── 200. Number of Islands.cpp │ ├── 39. Combination Sum.cpp │ ├── 90. Subsets II.cpp │ ├── 695. Max Area of Island.cpp │ ├── 216. Combination Sum III.cpp │ ├── 47. Permutations II.cpp │ ├── 1020. Number of Enclaves.cpp │ ├── 131. Palindrome Partitioning.cpp │ ├── 40. Combination Sum II.cpp │ ├── 1254. Number of Closed Islands.cpp │ ├── 17. Letter Combinations of a Phone Number.cpp │ ├── 51. N-Queens.cpp │ ├── 93. Restore IP Addresses.cpp │ └── 37. Sudoku Solver.cpp │ └── README.md ├── data structure └── Disjoint Set │ ├── figures │ ├── cp1.png │ ├── cp2.png │ ├── cp3.png │ ├── cp4.png │ ├── cp5.png │ ├── 2to1.png │ ├── 3to1.png │ ├── 4to1.png │ ├── 56to4.png │ ├── intro.png │ ├── mbr1.png │ ├── mbr2.png │ ├── mbr3.png │ ├── mbr4.png │ └── tree.png │ ├── src │ ├── Relative Problem.c │ └── Cheese.c │ └── README.md ├── OJ ├── LC-344. Reverse String │ └── reverse.cpp ├── LC-88. Merge Sorted Array │ └── sort.cpp ├── LC-387. First Unique Character in a String │ ├── findrfind.cpp │ └── outoftimelimit.cpp ├── LC-217. Contains Duplicate │ ├── bf_find.cpp │ ├── unordered_set.cpp │ └── sort.cpp ├── LC-Palindrome Number │ ├── digit_concern.cpp │ └── compare_positive_number.cpp ├── LC-561. Array Partition I │ └── sort.cpp ├── LC-1470. Shuffle the Array │ └── push_back.cpp ├── LC-1512. Number of Good Pairs │ └── bf.cpp ├── LC-1876. Substrings of Size Three with Distinct Characters │ └── bf_if.cpp ├── LC-26. Remove Duplicates from Sorted Array │ └── front2back.cpp ├── LC-53. Maximum Subarray │ ├── dynamic.cpp │ └── bf.cpp ├── LC-27. Remove Element │ └── sort.cpp ├── LC-643. Maximum Average Subarray I │ ├── lr.cpp │ └── slidingwindow.cpp ├── LC-2108. Find First Palindromic String in the Array │ └── reverse.cpp ├── LC-169. Majority Element │ └── forjump.cpp ├── Two-dimensional grid printing │ └── print_by_row.cpp ├── LC-219.Contains Duplicate II │ ├── doubleofloop.cpp │ ├── unordered_set.cpp │ └── vector.cpp ├── LC-1672. Richest Customer Wealth │ └── dfor.cpp ├── LC-389. Find the Difference │ └── sort.cpp ├── LC-769. Max Chunks To Make Sorted │ └── set.cpp ├── LC-1768. Merge Strings Alternately │ └── substr.cpp ├── LC-349. Intersection of Two Arrays │ └── setforonly.cpp ├── LC-1365. How Many Numbers Are Smaller Than the Current Number │ └── lambda.cpp ├── LC-1423. Maximum Points You Can Obtain from Cards │ ├── calmin.cpp │ └── doublepointer.cpp ├── LC-283. Move Zeroes │ └── for.cpp ├── LC-1389. Create Target Array in the Given Order │ └── deque.cpp ├── LC-1422. Maximum Score After Splitting a String │ └── vector.cpp ├── LC-205. Isomorphic Strings │ └── doublemap.cpp ├── LC-242. Valid Anagram │ └── transintovectosort.cpp ├── LC-922. Sort Array By Parity II │ └── tmpvector.cpp ├── LC-11. Container With Most Water │ └── doublepointer.cpp ├── LC-13. Roman to Integer │ └── unordered_map.cpp ├── LC-Reverse Integer │ ├── reverse.cpp │ └── stack.cpp ├── LC-448. Find All Numbers Disappeared in an Array │ └── setwithiterator.cpp ├── LC-383. Ransom Note │ └── unordered_set.cpp ├── LC-566. Reshape the Matrix │ └── forfor.cpp ├── LC-14. Longest Common Prefix │ └── first2last.cpp ├── LC-Two Sum │ ├── enumerate.cpp │ └── hash_table.cpp ├── LC-1047. Remove All Adjacent Duplicates In String │ └── stack.cpp ├── LC-1636. Sort Array by Increasing Frequency │ └── unordered_map.cpp ├── 剑指 Offer II 005. 单词长度的最大乘积 │ └── threefor.cpp ├── LC-496. Next Greater Element I │ └── whilefind.cpp ├── LC-234. Palindrome Linked List │ └── vector.cpp ├── LC-67. Add Binary │ └── back2frontadd.cpp ├── LC-151. Reverse Words in a String │ └── stack.cpp ├── LC-682. Baseball Game │ └── stack.cpp ├── LC-5. Longest Palindromic Substring │ ├── bf.cpp │ └── mirror.cpp ├── LC-506. Relative Ranks │ ├── unordered_map.cpp │ └── map.cpp ├── LC-3. Longest Substring Without Repeating Characters │ └── tmpvector.cpp ├── LC-1535. Find the Winner of an Array Game │ └── dequeandunordered_map.cpp ├── LC-1122. Relative Sort Array │ └── newvector.cpp ├── LC-345. Reverse Vowels of a String │ └── stack.cpp ├── LC-290. Word Pattern │ └── doublemap.cpp ├── LC-20. Valid Parentheses │ └── unordered_mapwithstack.cpp ├── LC-73. Set Matrix Zeroes │ └── tmpvv.cpp ├── LC-28. Implement strStr() │ └── tmpstringpushback.cpp ├── LC-347. Top K Frequent Elements │ └── multimap.cpp ├── LC-49. Group Anagrams │ └── unordered_map.cpp ├── LC-1694. Reformat Phone Number │ └── bysituation.cpp ├── LC-159.longest-substring-with-at-most-two-distinct-characters │ └── myslo.cpp ├── LC-1052. Grumpy Bookstore Owner │ └── flp.cpp ├── LC-1560. Most Visited Sector in a Circular Track │ └── map.cpp ├── OJ-69. a==b_ │ └── bythreecondition.cpp └── LC-638. Shopping Offers │ └── DFS.cpp └── others ├── Implement stack in C └── stack.c ├── Implement queue in C └── queue.c └── How to write if-else elegantly └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ -------------------------------------------------------------------------------- /string/KMP/figures/BF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/BF.png -------------------------------------------------------------------------------- /string/KMP/figures/prefix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/prefix.png -------------------------------------------------------------------------------- /string/KMP/figures/x!=now.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/x!=now.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # algorithm-problems 2 | 3 | This repository is used to store the collection of problems of algorithm. 4 | -------------------------------------------------------------------------------- /string/KMP/figures/move2try.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/move2try.png -------------------------------------------------------------------------------- /string/KMP/figures/next_intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/next_intro.png -------------------------------------------------------------------------------- /string/KMP/figures/ruler_move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/ruler_move.png -------------------------------------------------------------------------------- /string/KMP/figures/quick_final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/quick_final.png -------------------------------------------------------------------------------- /graph theory/DFS/figures/startidx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/graph theory/DFS/figures/startidx.png -------------------------------------------------------------------------------- /string/KMP/figures/1st_time_match.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/1st_time_match.png -------------------------------------------------------------------------------- /string/KMP/figures/how2movebynext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/how2movebynext.png -------------------------------------------------------------------------------- /string/KMP/figures/quick_solve_next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/string/KMP/figures/quick_solve_next.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/cp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/cp1.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/cp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/cp2.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/cp3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/cp3.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/cp4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/cp4.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/cp5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/cp5.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/2to1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/2to1.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/3to1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/3to1.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/4to1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/4to1.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/56to4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/56to4.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/intro.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/mbr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/mbr1.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/mbr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/mbr2.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/mbr3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/mbr3.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/mbr4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/mbr4.png -------------------------------------------------------------------------------- /data structure/Disjoint Set/figures/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryan4waters/algo/HEAD/data structure/Disjoint Set/figures/tree.png -------------------------------------------------------------------------------- /OJ/LC-344. Reverse String/reverse.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | void reverseString(vector& s) { 4 | reverse(s.begin(),s.end()); 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /OJ/LC-88. Merge Sorted Array/sort.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | void merge(vector& nums1, int m, vector& nums2, int n) { 4 | for (int i=0;i()); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /OJ/LC-387. First Unique Character in a String/findrfind.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int firstUniqChar(string s) { 4 | for (int i=0;i 3 | #include 4 | #include 5 | 6 | void func_toupper(char *dst, const char *src, int len) 7 | { 8 | for (int i = 0; i < len; ++i) { 9 | *dst++ = toupper(src[i]); 10 | } 11 | *dst = 0; 12 | } -------------------------------------------------------------------------------- /OJ/LC-217. Contains Duplicate/bf_find.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | bool containsDuplicate(vector& nums) { 4 | for (int i=0;i=2) {return true;} 6 | } 7 | return false; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /OJ/LC-387. First Unique Character in a String/outoftimelimit.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int firstUniqChar(string s) { 4 | for (int i=0;i((pow(2,31)-1)/10)){return 0;}//if not divided by 10, after the if-loop,res would overload 6 | f=x%10; 7 | res=res*10+f; 8 | x/=10; 9 | } 10 | return res; 11 | } 12 | -------------------------------------------------------------------------------- /OJ/LC-561. Array Partition I/sort.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int arrayPairSum(vector& nums) { 4 | int res=0; 5 | sort(nums.begin(),nums.end()); 6 | for (int i=0;i shuffle(vector& nums, int n) { 4 | vectorres; 5 | for (int i=0;i& nums) { 4 | unordered_set myset; 5 | for (auto c:nums){ 6 | if (myset.find(c)!=myset.end()){return true;} 7 | myset.insert(c); 8 | } 9 | return false; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /OJ/LC-217. Contains Duplicate/sort.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | bool containsDuplicate(vector& nums) { 4 | int len=nums.size(); 5 | sort(nums.begin(),nums.end()); 6 | for (int i=0;i 3 | #include 4 | #include 5 | 6 | void func_toupper(char *dst, const char *src, int len) 7 | { 8 | unsigned char ch; 9 | for (int i = 0; i < len; ++i) { 10 | ch = src[i]; 11 | *dst++ = (ch >= 'a' && ch <= 'z') ? (ch - 32) : ch; 12 | } 13 | *dst = 0; 14 | } -------------------------------------------------------------------------------- /string/toupper/src/c_003.c: -------------------------------------------------------------------------------- 1 | /*3.xor*/ 2 | #include 3 | #include 4 | #include 5 | 6 | void func_toupper(char *dst, const char *src, int len) 7 | { 8 | unsigned char ch; 9 | for (int i = 0; i < len; ++i) { 10 | ch = src[i]; 11 | *dst++ = (ch >= 'a' && ch <= 'z') ? ch^0x20 : ch; 12 | } 13 | *dst = 0; 14 | } -------------------------------------------------------------------------------- /OJ/LC-1512. Number of Good Pairs/bf.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int numIdenticalPairs(vector& nums) { 4 | int ct=0; 5 | for (int i=0;i& nums) { 4 | int len=nums.size(); 5 | if (len < 2) {return len;} 6 | int c=1; 7 | for (int j=0;j& nums) { 4 | int len=nums.size(); 5 | 6 | vector vec(len); 7 | vec[0]=nums[0]; 8 | int res=vec[0]; 9 | 10 | for (int i=1;i& nums, int val) { 4 | int ct=0; 5 | for (auto &c:nums) { 6 | if (c == val) { 7 | c=-1; 8 | ++ct; 9 | } 10 | } 11 | sort(nums.begin(),nums.end(),greater()); 12 | return nums.size()-ct; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /OJ/LC-643. Maximum Average Subarray I/lr.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | double findMaxAverage(vector& nums, int k) { 4 | double fres=accumulate(nums.begin(),nums.begin()+k,0); 5 | double tmp=fres; 6 | for (int i=0;i& words) { 4 | string tmp=""; 5 | for (int i=0;i& nums) { 4 | int ct=0; 5 | sort(nums.begin(),nums.end()); 6 | for (int i=0;i nums.size()/2) {return nums[i];} 9 | else {i+=ct;} 10 | } 11 | return -1; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /OJ/LC-643. Maximum Average Subarray I/slidingwindow.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | double findMaxAverage(vector& nums, int k) { 4 | int len=nums.size(); 5 | int sum=accumulate(nums.begin(),nums.begin()+k,0); 6 | int res=sum; 7 | for (int i=1;i=res) {res=sum;}//res=max(res,sum); 10 | } 11 | return (double)res/k; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /OJ/Two-dimensional grid printing/print_by_row.cpp: -------------------------------------------------------------------------------- 1 | int main(){ 2 | int n,m; 3 | cin>>n>>m; 4 | string s1="+---"; 5 | string s2="| "; 6 | 7 | for(int j=0;j<(2*n+1);j++){ 8 | 9 | if((j%2)==0){ 10 | for(int i=0;i& nums, int k) { 4 | int m=0; 5 | int len=nums.size(); 6 | 7 | for (int i=0;i& nums) { 4 | if (nums.size()==1) {return nums[0];} 5 | int max=INT_MIN; 6 | for (int i=0;imax){max=sum;} 11 | } 12 | } 13 | return max; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /OJ/LC-1672. Richest Customer Wealth/dfor.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int maximumWealth(vector>& accounts) { 4 | int res=0; 5 | int tmp=0; 6 | for (int i=0;i& nums, int k) { 4 | int len=nums.size(); 5 | unordered_set myset; 6 | 7 | for (int i=0;ik){myset.erase(nums[i - k]);}//set is NOT equal to vector, .begin() or .end() can't be used. 11 | } 12 | return false; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /OJ/LC-Palindrome Number/compare_positive_number.cpp: -------------------------------------------------------------------------------- 1 | int main(){ 2 | int x; 3 | cin>>x; 4 | 5 | if (x<0){cout<<"false";} 6 | else if (x==0){cout<<"true";} 7 | 8 | else { 9 | int len=log10(x)+1; 10 | string s=to_string(x); 11 | int j=0; 12 | 13 | for (int i=0;i<(len/2);i++){ 14 | if (s[i]==s[len-i-1]){j++;} 15 | } 16 | if (j==(len/2)){cout<<"true";} 17 | else {cout<<"flase";} 18 | } 19 | 20 | system ("pause"); 21 | } 22 | -------------------------------------------------------------------------------- /OJ/LC-769. Max Chunks To Make Sorted/set.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int maxChunksToSorted(vector& arr) { 4 | int res = 0; 5 | settmp; 6 | for (int i=0;iword2.size()) {s=s+word1.substr(word2.size());} 10 | if (word1.size() intersection(vector& nums1, vector& nums2) { 4 | sets1; 5 | vectorv; 6 | for (auto c:nums1) {s1.insert(c);} 7 | int it=0; 8 | while (it < nums2.size()) { 9 | if (find(s1.begin(),s1.end(),nums2[it]) != s1.end()) {v.push_back(nums2[it]);s1.erase(nums2[it]);} 10 | it++; 11 | } 12 | return v; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /OJ/LC-1365. How Many Numbers Are Smaller Than the Current Number/lambda.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector smallerNumbersThanCurrent(vector& nums) { 4 | vectorres; 5 | int tmp=0; 6 | for (int i=0;ibool{ 8 | return a& cardPoints, int k) { 4 | int ct=cardPoints.size()-k; 5 | vector::iterator it=cardPoints.begin(); 6 | int res=accumulate(it,it+ct,0); 7 | int tmp=res; 8 | for (int i=0;i& nums) { 4 | int onum=count(nums.begin(),nums.end(),0); 5 | int unonum=nums.size()-onum; 6 | int ct=0; 7 | for (int i=0;i createTargetArray(vector& nums, vector& index) { 4 | int len=index.size(); 5 | dequemydeque; 6 | vectorres; 7 | for (int i=0;iv(len-1); 6 | int num0=0; 7 | int num1=0; 8 | for (int i=0;i()); 14 | return v[0]; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /OJ/LC-205. Isomorphic Strings/doublemap.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | bool isIsomorphic(string s, string t) { 4 | unordered_mapst; 5 | unordered_mapts; 6 | for (int i=0;ivs; 5 | vectorvt; 6 | for (int i=0;i sortArrayByParityII(vector& nums) { 4 | vectoros; 5 | vectorjs; 6 | vectorres; 7 | for(int i=0;i& height) { 4 | int lp=0; 5 | int rp=height.size()-1; 6 | int res=min(height[lp],height[rp]) * (rp-lp); 7 | while (lp < rp) { 8 | if (height[lp] < height[rp]) { 9 | res=max(res,min(height[++lp],height[rp]) * (rp-lp)); 10 | } else { 11 | res=max(res,min(height[lp],height[--rp]) * (rp-lp)); 12 | } 13 | } 14 | return res; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /OJ/LC-13. Roman to Integer/unordered_map.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int romanToInt(string s) { 4 | int len=s.size(); 5 | int addin=0; 6 | unordered_map mymap={ 7 | {'I',1}, 8 | {'V',5}, 9 | {'X',10}, 10 | {'L',50}, 11 | {'C',100}, 12 | {'D',500}, 13 | {'M',1000}, 14 | }; 15 | 16 | for (int i=0;i>x; 4 | vector vec; 5 | 6 | if (x==0){cout< findDisappearedNumbers(vector& nums) { 4 | sets; 5 | vectorv; 6 | int len=nums.size(); 7 | for (int i=0;i::iterator it=s.begin(); 13 | if (*it == j) {s.erase(*it);} 14 | else {v.push_back(j);} 15 | } 16 | return v; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /OJ/LC-383. Ransom Note/unordered_set.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | bool canConstruct(string ransomNote, string magazine) { 4 | unordered_setmyset; 5 | for (const auto&c:ransomNote) { 6 | myset.insert(c); 7 | } 8 | auto it=myset.begin(); 9 | while (it != myset.end()) { 10 | if (count(ransomNote.begin(),ransomNote.end(),*it) > count(magazine.begin(),magazine.end(),*it)) { 11 | return false; 12 | } 13 | ++it; 14 | } 15 | return true; 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /OJ/LC-566. Reshape the Matrix/forfor.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> matrixReshape(vector>& mat, int r, int c) { 4 | int m=mat.size(); 5 | int n=mat[0].size(); 6 | if (m*n!=r*c){return mat;} 7 | 8 | vector> oldvec(r,vector(c,0)); 9 | int row=0; 10 | int col=0; 11 | for (int i=0;i& strs) { 4 | if(strs.size() == 0) {return "";}; 5 | 6 | string scom=strs[0]; 7 | string stmp; 8 | for (int i=1;i& cardPoints, int k) { 4 | int num=cardPoints.size(); 5 | int tmp=accumulate(cardPoints.end()-k,cardPoints.end(),0); // put the window at the end place 6 | int res=tmp; // initialize the result 7 | int l=num-k; // left pointer 8 | int r=num-1; // right pointer 9 | while (l < num) { 10 | tmp=tmp-cardPoints[l++%num]+cardPoints[++r%num]; 11 | res=max(tmp,res); 12 | } 13 | return res; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /OJ/LC-Two Sum/enumerate.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector twoSum(vector& nums, int target) { 4 | int len=nums.size(); 5 | for (int i=0;i tmp; 3 | int b=abs(a); 4 | string s=to_string(b); 5 | string res; 6 | int len=s.size(); 7 | for (int i=0;i>x; 24 | if (x==0){cout<0){cout< 2 | #include 3 | #include 4 | #include 5 | 6 | extern void func_toupper(char *dst, const char *src, int len); 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | char *src = "RWAGeNIsfdHOGJBALdfsKVNXesdfUEYORdsfEEhQSDSDFYKTUKKDFsSfDFDmnVZdsfDSVBFPRQNKNFASRW"; 11 | 12 | char dst[256]; 13 | 14 | if (argc != 2) { 15 | printf("Usage: char_conv [times]\n"); 16 | return -1; 17 | } 18 | int times = atoi(argv[1]); 19 | for (int i = 0; i< times; ++i) { 20 | func_toupper(dst, src, strlen(src)); 21 | } 22 | 23 | (void)dst; 24 | return 0; 25 | } -------------------------------------------------------------------------------- /OJ/LC-1047. Remove All Adjacent Duplicates In String/stack.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | string removeDuplicates(string s) { 4 | string res=""; 5 | stackmystack; 6 | for (auto ss:s) { 7 | if (mystack.empty() || ss != mystack.top()) { 8 | mystack.push(ss); 9 | cout< frequencySort(vector& nums) { 4 | unordered_mapmymap; 5 | int ct=0; 6 | sort(nums.begin(),nums.end()); 7 | for (int i=0;ib;} 14 | else {return mymap[a]& words) { 4 | int ct=0; 5 | int res=0; 6 | for (int i=0;i res) {res=ct*words[j].size();} 14 | } 15 | } 16 | return res; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /graph theory/DFS/src/78. Subsets.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> subsets(vector& nums) { 4 | vector>res; 5 | vectortmp; 6 | int idx = 0; 7 | dfs(nums, tmp, res, idx); 8 | return res; 9 | } 10 | void dfs(vector& nums, vector& tmp, vector>& res, int idx) { 11 | res.emplace_back(tmp); 12 | if (idx > nums.size() - 1) { 13 | return; 14 | } 15 | for (int i = idx; i < nums.size(); ++i) { 16 | tmp.emplace_back(nums[i]); 17 | dfs(nums, tmp, res, i + 1); 18 | tmp.pop_back(); 19 | } 20 | } 21 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/77. Combinations.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> combine(int n, int k) { 4 | vector>res; 5 | vectortmp; 6 | int idx = 1; 7 | dfs(n, k, res, tmp, idx); 8 | return res; 9 | } 10 | void dfs(const int& n, const int& k, vector>& res, vector& tmp, int idx) { 11 | if (tmp.size() == k) { 12 | res.emplace_back(tmp); 13 | return; 14 | } 15 | for (int i = idx;i <= n - (k - tmp.size()) + 1; ++i) { 16 | tmp.emplace_back(i); 17 | dfs(n, k, res, tmp, i + 1); 18 | tmp.pop_back(); 19 | } 20 | } 21 | }; -------------------------------------------------------------------------------- /OJ/LC-496. Next Greater Element I/whilefind.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector nextGreaterElement(vector& nums1, vector& nums2) { 4 | int len1=nums1.size(); 5 | int len2=nums2.size(); 6 | vectorres(len1); 7 | vector::iterator it=nums2.begin(); 8 | for (int i=0;i nums1[i]) {res[i]=*it;break;} 13 | it++; 14 | if (it == nums2.end()) {res[i]=-1;} 15 | } 16 | } 17 | return res; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /OJ/LC-Two Sum/hash_table.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector twoSum(vector& nums, int target) { 4 | unordered_map hashtable; 5 | for (int i = 0; i < nums.size(); ++i) { 6 | auto it = hashtable.find(target - nums[i]); 7 | if (it != hashtable.end()) { 8 | return {it->second, i}; 9 | } 10 | hashtable[nums[i]] = i; 11 | } 12 | return {}; 13 | } 14 | }; 15 | 16 | /* 17 | Time Complexity: O(N), where N is the number of elements in the array. For each element x, we can find target-x by O(1). 18 | 19 | Space Complexity: O(N), where N is the number of elements in the array. Mainly the overhead of the hash table. 20 | */ 21 | -------------------------------------------------------------------------------- /string/toupper/src/makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -static -O2 -ftree-vectorize 3 | # -static is used so that the executable file does not depend on external libraries, so that the data obtained is more accurate. 4 | 5 | all : c_001 c_002 c_003 c_004 c_005 c_006 6 | .PHONY : all 7 | 8 | c_001 : main.c c_001.c 9 | $(CC) -o c_001 main.c c_001.c $(CFLAGS) 10 | 11 | c_002 : main.c c_002.c 12 | $(CC) -o c_002 main.c c_002.c $(CFLAGS) 13 | 14 | c_003 : main.c c_003.c 15 | $(CC) -o c_003 main.c c_003.c $(CFLAGS) 16 | 17 | c_004 : main.c c_004.c 18 | $(CC) -o c_004 main.c c_004.c $(CFLAGS) 19 | 20 | c_005 : main.c c_005.c 21 | $(CC) -o c_005 main.c c_005.c $(CFLAGS) 22 | 23 | c_006: main.c c_006.c 24 | $(CC) -o c_006 main.c c_006.c $(CFLAGS) 25 | -------------------------------------------------------------------------------- /OJ/LC-234. Palindrome Linked List/vector.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | * struct ListNode { 4 | * int val; 5 | * ListNode *next; 6 | * ListNode() : val(0), next(nullptr) {} 7 | * ListNode(int x) : val(x), next(nullptr) {} 8 | * ListNode(int x, ListNode *next) : val(x), next(next) {} 9 | * }; 10 | */ 11 | class Solution { 12 | public: 13 | bool isPalindrome(ListNode* head) { 14 | vectorv; 15 | while (head != nullptr) { 16 | v.push_back(head->val); 17 | head=head->next; 18 | } 19 | vectortmpv=v; 20 | reverse(tmpv.begin(),tmpv.end()); 21 | if (v == tmpv) {return true;} 22 | else {return false;} 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /OJ/LC-67. Add Binary/back2frontadd.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | class Solution { 4 | public: 5 | string addBinary(string a, string b) { 6 | 7 | int alen=a.size(); 8 | int blen=b.size(); 9 | if (a.size()>b.size()) { 10 | for(int m=0;ma.size()) { 13 | for(int n=0;n=1;i--){ 17 | a[i]=a[i]-'0'+b[i]; 18 | if ((a[i]-'0')>1){a[i]=(a[i]-'0')%2+'0';a[i-1]+=1;}//I replace 1 with (a[i]-'0')/2 and the result would be wrong 19 | } 20 | a[0]=a[0]-'0'+b[0]; 21 | 22 | if (a[0]-'0'==2) {a[0]='0';a='1'+a;} 23 | if (a[0]-'0'==3) {a[0]='1';a='1'+a;} 24 | 25 | return a; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /string/KMP/src/next_solve_init_demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | int main() 9 | { 10 | string p = "abcdabd"; 11 | vectornext(p.size(), 0); 12 | for (int pr = p.size() - 1; pr > 0; --pr) { 13 | for (int pl = pr; pl > 0; --pl) { 14 | int i = 0; 15 | for (int pos = pl; pos <= pr; ++pos, ++i) { 16 | if (p[i] != p[pos]) { 17 | break; 18 | } 19 | } 20 | if (i == pr - pl + 1) { 21 | next[pr] = max(next[pr], i); 22 | } 23 | } 24 | } 25 | for_each(next.begin(), next.end(), [&](int val){ 26 | cout << val << " "; 27 | }); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /OJ/LC-151. Reverse Words in a String/stack.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | string reverseWords(string s) { 4 | s+=' '; // to push the last word 5 | string res=""; 6 | string tmp=""; 7 | stackmystack; 8 | for (int i=0;i& ops) { 4 | int len=ops.size(); 5 | int res=0; 6 | stackmystack; 7 | for (int i=0;imyset; 6 | string tmp=""; 7 | string ttmp=""; 8 | int len=s.size(); 9 | for (int i=0;i::iterator jj; 19 | for (auto j=myset.begin();j != myset.end();j++) { 20 | if ((*j).size() > ss) {ss=(*j).size();jj=j;} 21 | } 22 | return *jj; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /graph theory/DFS/src/46. Permutations.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> permute(vector& nums) { 4 | vector>res; 5 | vectorpath; 6 | dequeused(nums.size(), false); 7 | dfs(nums, path, used, res); 8 | return res; 9 | } 10 | void dfs(const vector&nums, vector&path, deque&used, vector>&res) { 11 | if (path.size() == nums.size()) { 12 | res.emplace_back(path); 13 | return; 14 | } 15 | for (int i = 0; i < nums.size(); ++i) { 16 | if (used[i]) continue; 17 | path.emplace_back(nums[i]); 18 | used[i] = true; 19 | dfs(nums, path, used, res); 20 | used[i] = false; 21 | path.pop_back(); 22 | } 23 | } 24 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/200. Number of Islands.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int numIslands(vector>& grid) { 4 | int res = 0; 5 | for (int i = 0; i < grid.size(); ++i) { 6 | for (int j = 0; j < grid[0].size(); ++j) { 7 | if (grid[i][j] == '0') continue; 8 | ++res; 9 | dfs(grid, i, j); 10 | } 11 | } 12 | return res; 13 | } 14 | void dfs(vector>& grid, const int& row, const int& col) { 15 | if ((row < 0) || (row >= grid.size()) || (col < 0) || (col >= grid[0].size())) return; 16 | if (grid[row][col] == '0') return; 17 | grid[row][col] = '0'; 18 | dfs(grid, row, col + 1); 19 | dfs(grid, row, col - 1); 20 | dfs(grid, row + 1, col); 21 | dfs(grid, row - 1, col); 22 | } 23 | }; -------------------------------------------------------------------------------- /OJ/LC-506. Relative Ranks/unordered_map.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector findRelativeRanks(vector& score) { 4 | vectortmp=score; 5 | int len=score.size(); 6 | vectorres; 7 | if (len == 1) {return {"Gold Medal"};} 8 | sort(tmp.begin(),tmp.end(),greater()); 9 | unordered_maprank(len); 10 | rank[tmp[0]]="Gold Medal"; 11 | rank[tmp[1]]="Silver Medal"; 12 | if (len >= 3) { 13 | rank[tmp[2]]="Bronze Medal"; 14 | for (int i=3;i& nums, int k) { 4 | vectorvec; 5 | int len=nums.size(); 6 | if (len==1 || k==0) {return false;} 7 | /* 8 | *I used vec.begin() to compare, unfortunately it can't check the last few elements in one for-loop, I believe have to add one another loop to check the last group. 9 | *So directly counting the element[i] is better with less confusing to calculate the rule of for-loop, besides, never would worry about the last group. 10 | */ 11 | for (int i=0;ik) {vec.erase(vec.begin());} 15 | } 16 | return false; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /OJ/LC-3. Longest Substring Without Repeating Characters/tmpvector.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int lengthOfLongestSubstring(string s) { 4 | if (s == "") {return 0;} 5 | if (s.size() == 1) {return 1;} 6 | vectorv; 7 | vectorres; 8 | int tmp=0; 9 | int ct=0; 10 | for (int i=0;i()); 25 | return res[0]; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /graph theory/DFS/src/39. Combination Sum.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> combinationSum(vector& candidates, int target) { 4 | vector>res; 5 | vectortmp; 6 | int idx = 0; 7 | dfs(candidates, target, tmp, res, idx); 8 | return res; 9 | } 10 | void dfs(const vector& candidates, const int& target, vector& tmp, vector>& res, int idx) { 11 | if (accumulate(tmp.begin(), tmp.end(), 0) > target) return; 12 | if (accumulate(tmp.begin(), tmp.end(), 0) == target) { 13 | res.emplace_back(tmp); 14 | return; 15 | } 16 | for (int i = idx; i < candidates.size(); ++i) { 17 | tmp.emplace_back(candidates[i]); 18 | dfs(candidates, target, tmp, res, i); 19 | tmp.pop_back(); 20 | } 21 | } 22 | }; -------------------------------------------------------------------------------- /string/KMP/src/kmp_python_ver.py: -------------------------------------------------------------------------------- 1 | s = input().strip() 2 | p = input().strip() 3 | 4 | nxt = [] 5 | 6 | def buildNxt(): 7 | nxt.append(0) 8 | x = 1 9 | now = 0 10 | 11 | while x < len(p): 12 | if p[now] == p[x]: 13 | n += 1 14 | x += 1 15 | nxt.append(now) 16 | elif now: 17 | now = nxt[now - 1] 18 | else: 19 | nxt.append(0) 20 | x += 1 21 | 22 | def search(): 23 | tar = 0 24 | pos = 0 25 | 26 | while tar < len(s): 27 | if s[tar] == p[pos]: 28 | tar += 1 29 | pos += 1 30 | elif pos: 31 | pos = nxt[pos - 1] 32 | else: 33 | tar += 1 34 | 35 | if pos == len(p): 36 | print(tar - pos + 1) 37 | pos =nxt[pos - 1] 38 | 39 | buildNxt() 40 | search() 41 | print(' '.join(map(str, nxt))) -------------------------------------------------------------------------------- /graph theory/DFS/src/90. Subsets II.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> subsetsWithDup(vector& nums) { 4 | sort(nums.begin(), nums.end()); 5 | vector>res; 6 | vectortmp; 7 | int idx = 0; 8 | dfs(nums, tmp, res, idx); 9 | return res; 10 | } 11 | void dfs(vector& nums, vector& tmp, vector>& res, int idx) { 12 | unordered_sets; 13 | res.emplace_back(tmp); 14 | if (idx > nums.size() - 1) { 15 | return; 16 | } 17 | for (int i = idx; i < nums.size(); ++i) { 18 | if (s.find(nums[i]) != s.end()) { 19 | continue; 20 | } 21 | tmp.emplace_back(nums[i]); 22 | s.insert(nums[i]); 23 | dfs(nums, tmp, res, i + 1); 24 | tmp.pop_back(); 25 | } 26 | } 27 | }; -------------------------------------------------------------------------------- /OJ/LC-1535. Find the Winner of an Array Game/dequeandunordered_map.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int getWinner(vector& arr, int k) { 4 | if (arr.size()-1 < k) { 5 | sort(arr.begin(),arr.end(),greater<>()); 6 | return arr[0]; 7 | } 8 | dequemydeque; 9 | for (const auto a:arr) { 10 | mydeque.push_back(a); 11 | } 12 | unordered_mapmymap; 13 | int tmpmax=0; 14 | int tmpmin=0; 15 | for (;1;) { 16 | tmpmax=max(mydeque[0],mydeque[1]); 17 | tmpmin=min(mydeque[0],mydeque[1]); 18 | ++mymap[tmpmax]; 19 | auto dpos=mydeque[0]>& grid) { 4 | int res = 0; 5 | int tmp =0; 6 | for (int i = 0; i < grid.size(); ++i) { 7 | for (int j = 0; j < grid[0].size(); ++j) { 8 | if (grid[i][j] == 0) continue; 9 | tmp = dfs(grid, i, j); 10 | res = max(res, tmp); 11 | } 12 | } 13 | return res; 14 | } 15 | int dfs(vector>& grid, const int& row, const int& col) { 16 | if ((row < 0) || (col < 0) || (row >= grid.size()) || (col >= grid[0].size()) || (grid[row][col] == 0)) 17 | return 0; 18 | grid[row][col] = 0; 19 | return dfs(grid,row,col + 1) + 20 | dfs(grid, row, col - 1) + 21 | dfs(grid, row + 1, col) + 22 | dfs(grid, row - 1, col) + 1; 23 | } 24 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/216. Combination Sum III.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector>res; 4 | vectortmp; 5 | vector> combinationSum3(int k, int n) { 6 | vectornums{1, 2, 3, 4, 5, 6, 7, 8, 9}; 7 | int target = n; 8 | dfs(nums, target, k, 0); 9 | return res; 10 | } 11 | void dfs(const vector& nums, const int& target, const int& k, int idx) { 12 | if ((tmp.size() == k) && (accumulate(tmp.begin(), tmp.end(), 0) == target)) { 13 | res.emplace_back(tmp); 14 | return; 15 | } 16 | if ((tmp.size() > k) || (accumulate(tmp.begin(), tmp.end(), 0) > target)) return; 17 | for (int i = idx; i < nums.size(); ++i) { // check before, cut right brunch 18 | tmp.emplace_back(nums[i]); 19 | dfs(nums, target, k, i + 1); 20 | tmp.pop_back(); 21 | } 22 | } 23 | }; -------------------------------------------------------------------------------- /OJ/LC-1122. Relative Sort Array/newvector.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector relativeSortArray(vector& arr1, vector& arr2) { 4 | int len1=arr1.size(); 5 | int len2=arr2.size(); 6 | int ncount=0;; 7 | int cc=0; 8 | int nall=0; 9 | vector arr3=arr1; 10 | for (int i=0;i()); 26 | return arr3; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /OJ/LC-345. Reverse Vowels of a String/stack.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | string reverseVowels(string s) { 4 | stackms=myjudge(s); 5 | for (int j=0;j myjudge(string str) { 15 | stackmystack; 16 | for (int i=0;imcs; 5 | unordered_mapmsc; 6 | vectorv; 7 | string tmp=""; 8 | stringstream ss(s); 9 | while (getline(ss,tmp,' ')) { // spilt the string 10 | v.emplace_back(tmp); 11 | } 12 | if (v.size() != pattern.size()) { 13 | return false; 14 | } 15 | for (int i=0;i> permuteUnique(vector& nums) { 4 | vector>res; 5 | vectortmp; 6 | dequeused(nums.size(), false); 7 | dfs(nums, res, tmp, used); 8 | return res; 9 | } 10 | void dfs(const vector& nums, vector>& res, vector& tmp, deque& used) { 11 | if (tmp.size() == nums.size()) { 12 | for (const auto& c:res) { 13 | if (c == tmp) return; 14 | } 15 | res.emplace_back(tmp); 16 | return; 17 | } 18 | for (int i = 0; i < nums.size(); ++i) { 19 | if (used[i]) continue; 20 | tmp.emplace_back(nums[i]); 21 | used[i] = true; 22 | dfs(nums, res, tmp, used); 23 | used[i] = false; 24 | tmp.pop_back(); 25 | } 26 | } 27 | }; -------------------------------------------------------------------------------- /OJ/LC-20. Valid Parentheses/unordered_mapwithstack.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | bool isValid(string s) { 4 | //deal with the special terms 5 | int len=s.size(); 6 | if (len%2 != 0) {return false;} 7 | if (s[0]==')' || s[0]=='}' || s[0]==']') {return false;} 8 | if (s=="()" || s=="{}" || s=="[]") {return true;} 9 | 10 | //build unordered_map to match different char with key value 11 | unordered_map mymap={ 12 | {'(',1}, 13 | {'{',2}, 14 | {'[',3}, 15 | {')',4}, 16 | {'}',5}, 17 | {']',6}, 18 | }; 19 | stack mystack; 20 | 21 | //traverse s to dump off matchup 22 | for (auto c:s){ 23 | int flag=mymap[c]; 24 | if (flag<=3){mystack.push(c);} 25 | else if (!mystack.empty() && flag==mymap[mystack.top()]+3) {mystack.pop();} 26 | else {return false;} 27 | } 28 | if (mystack.empty()) {return true;} 29 | else {return false;} 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /OJ/LC-73. Set Matrix Zeroes/tmpvv.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | void setZeroes(vector>& matrix) { 4 | int rownum=matrix.size(); 5 | int colnum=matrix[0].size(); 6 | vector>v; 7 | for (int m=0;m(colnum)); 9 | } 10 | for (int i=0;i 2 | #define MAXN 5005 3 | int fa[MAXN], rank[MAXN]; 4 | inline void init(int n) 5 | { 6 | for (int i = 1; i <= n; ++i) 7 | { 8 | fa[i] = i; 9 | rank[i] = 1; 10 | } 11 | } 12 | int find(int x) 13 | { 14 | return x == fa[x] ? x : (fa[x] = find(fa[x])); 15 | } 16 | inline void merge(int i, int j) 17 | { 18 | int x = find(i), y = find(j); 19 | if (rank[x] <= rank[y]) 20 | fa[x] = y; 21 | else 22 | fa[y] = x; 23 | if (rank[x] == rank[y] && x != y) 24 | rank[y]++; 25 | } 26 | int main() 27 | { 28 | int n, m, p, x, y; 29 | scanf("%d%d%d", &n, &m, &p); 30 | init(n); 31 | for (int i = 0; i < m; ++i) 32 | { 33 | scanf("%d%d", &x, &y); 34 | merge(x, y); 35 | } 36 | for (int i = 0; i < p; ++i) 37 | { 38 | scanf("%d%d", &x, &y); 39 | printf("%s\n", find(x) == find(y) ? "Yes" : "No"); 40 | } 41 | return 0; 42 | } -------------------------------------------------------------------------------- /OJ/LC-28. Implement strStr()/tmpstringpushback.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int strStr(string haystack, string needle) { 4 | if (needle=="") {return 0;} 5 | if (needle.size()>haystack.size()) {return -1;} 6 | 7 | int num=0; 8 | int n=0; 9 | if (needle.size()==haystack.size()) { 10 | for (int i=0;i topKFrequent(vector& nums, int k) { 4 | multimapm; 5 | setsct; 6 | int ct=0; 7 | sort(nums.begin(),nums.end()); 8 | for (int i=0;ires; 15 | for (auto it=sct.begin();it!=sct.end();++it) { 16 | auto dit=m.equal_range(*it); 17 | if (dit.first != end(m)) { 18 | for (auto eit=dit.first;eit != dit.second;++eit) { 19 | res.emplace_back(eit->second); 20 | } 21 | } 22 | } 23 | vectorfin; 24 | for (auto iter=res.end()-1;iter!=res.end()-k-1;--iter) { 25 | fin.emplace_back(*iter); 26 | } 27 | return fin; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /OJ/LC-49. Group Anagrams/unordered_map.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> groupAnagrams(vector& strs) { 4 | vector>res; 5 | unordered_mapm; 6 | vectord; 7 | string tmps=""; 8 | for (int i=0;i()); 20 | res[col].emplace_back(m[j]); 21 | for (int k=j+1;k findRelativeRanks(vector& score) { 4 | mapm; 5 | m.insert(pair(0,"Gold Medal")); 6 | m.insert(pair(1,"Silver Medal")); 7 | m.insert(pair(2,"Bronze Medal")); 8 | vectortmp=score; 9 | vectorres(score.size()); 10 | sort(tmp.begin(),tmp.end(),greater()); 11 | int mymark=0; 12 | for (int i=0;i&v1,vector&v2,int x) { 21 | int cct=0; 22 | auto it=find(v1.begin(),v1.end(),v2[x]); 23 | auto itct=v1.begin(); 24 | while (itct != it) { 25 | itct++; 26 | cct++; 27 | } 28 | return cct; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /OJ/LC-1694. Reformat Phone Number/bysituation.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | string reformatNumber(string number) { 4 | int len1=number.size(); 5 | if (len1 <= 3) {return number;} 6 | string s=""; 7 | for (const auto &c:number) { 8 | if (c != ' ' && c != '-') { 9 | s=s+c+' '; 10 | } 11 | } 12 | int len2=s.size(); 13 | if (len2 % 3 == 0) { 14 | for (int i=0;i>& grid) { 4 | int res = 0; 5 | for (int i = 0; i < grid.size(); ++i) { 6 | dfs(grid, i, 0); 7 | dfs(grid, i, grid[0].size() - 1); 8 | } 9 | for (int j = 0; j < grid[0].size(); ++j) { 10 | dfs(grid, 0, j); 11 | dfs(grid, grid.size() - 1, j); 12 | } 13 | for (int r = 0; r < grid.size(); ++r) { 14 | for (int c = 0; c < grid[0].size(); ++c) { 15 | if (grid[r][c] == 0) continue; 16 | ++res; 17 | } 18 | } 19 | return res; 20 | } 21 | void dfs(vector>& grid, const int& r, const int& c) { 22 | if ((r < 0) || (c < 0) || (r >= grid.size()) || (c >= grid[0].size()) || (grid[r][c] == 0)) 23 | return; 24 | grid[r][c] = 0; 25 | dfs(grid, r, c + 1); 26 | dfs(grid, r, c - 1); 27 | dfs(grid, r + 1, c); 28 | dfs(grid, r - 1, c); 29 | } 30 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/131. Palindrome Partitioning.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> partition(string s) { 4 | vector>res; 5 | vectortmp; 6 | int idx = 0; 7 | dfs(s, res, tmp, idx); 8 | return res; 9 | } 10 | void dfs(const string& s, vector>& res, vector& tmp, int idx) { 11 | if (idx == s.size()) { 12 | res.emplace_back(tmp); 13 | return; 14 | } 15 | for (int i = idx; i < s.size(); ++i) { 16 | if (!isgoback(s, idx, i)) continue; 17 | else { 18 | string str = s.substr(idx, i - idx + 1); 19 | tmp.emplace_back(str); 20 | dfs(s, res, tmp, i + 1); 21 | tmp.pop_back(); 22 | } 23 | } 24 | } 25 | bool isgoback(const string& s, const int& l, const int& r) { 26 | for (int i = l, j = r; i < j; ++i, --j) { 27 | if (s[i] != s[j]) return false; 28 | } 29 | return true; 30 | } 31 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/40. Combination Sum II.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> combinationSum2(vector& candidates, int target) { 4 | sort(candidates.begin(), candidates.end()); 5 | vector>res; 6 | vectortmp; 7 | int idx = 0; 8 | dfs(candidates, target, res, tmp, idx); 9 | return res; 10 | } 11 | void dfs(const vector& candidates, const int& target, vector>& res, vector& tmp, int idx) { 12 | unordered_setmyset; 13 | if (accumulate(tmp.begin(), tmp.end(), 0) == target) { 14 | res.emplace_back(tmp); 15 | return; 16 | } 17 | for (int i = idx; (i < candidates.size()) && (accumulate(tmp.begin(), tmp.end(), candidates[i]) <= target); ++i) { 18 | if (myset.find(candidates[i]) != myset.end()) continue; 19 | tmp.emplace_back(candidates[i]); 20 | myset.insert(candidates[i]); 21 | dfs(candidates, target, res, tmp, i + 1); 22 | tmp.pop_back(); 23 | } 24 | } 25 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/1254. Number of Closed Islands.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int closedIsland(vector>& grid) { 4 | for (int i = 0; i < grid.size(); ++i) { 5 | dfs(grid, i, 0); 6 | dfs(grid, i, grid[0].size() - 1); 7 | } 8 | for (int j = 0; j < grid[0].size(); ++j) { 9 | dfs(grid, 0, j); 10 | dfs(grid, grid.size() - 1, j); 11 | } 12 | int res = 0; 13 | for (int r = 0; r < grid.size(); ++r) { 14 | for (int c = 0; c < grid[0].size(); ++c) { 15 | if (grid[r][c] == 1) continue; 16 | ++res; 17 | dfs(grid, r, c); 18 | } 19 | } 20 | return res; 21 | } 22 | void dfs(vector>& grid, const int& row, const int& col) { 23 | if ((row < 0) || (col < 0) || (row >= grid.size()) || (col >= grid[0].size()) || (grid[row][col] == 1)) 24 | return; 25 | grid[row][col] = 1; 26 | dfs(grid, row, col + 1); 27 | dfs(grid, row, col - 1); 28 | dfs(grid, row + 1, col); 29 | dfs(grid, row - 1, col); 30 | } 31 | }; -------------------------------------------------------------------------------- /OJ/LC-159.longest-substring-with-at-most-two-distinct-characters/myslo.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | int lengthOfLongestSubstringTwoDistinct(string s) { 4 | string tmps=" "; 5 | for (const auto& c:s) { 6 | if (c != tmps[tmps.size()-1]) { 7 | tmps += ' '; 8 | } 9 | tmps += c; 10 | } 11 | tmps = tmps.substr(2); 12 | stringstream ss(tmps); 13 | string os=""; 14 | vectorvct; 15 | vectorvc; 16 | while (getline(ss,os,' ')) { 17 | vct.emplace_back(os.size()); 18 | vc.emplace_back(os[0]); 19 | } 20 | setmyset; 21 | int tmp=0; 22 | int res=0; 23 | for (int i=0;i 2) { 27 | break; 28 | } 29 | tmp=accumulate(vct.begin()+i,vct.begin()+j+1,0); 30 | res=max(tmp,res); 31 | } 32 | myset.clear(); 33 | } 34 | return res; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /graph theory/DFS/src/17. Letter Combinations of a Phone Number.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector letterCombinations(string digits) { 4 | vectorres; 5 | if (digits.empty()) return res; 6 | mapm; 7 | m.insert(make_pair('2', "abc")); 8 | m.insert(make_pair('3', "def")); 9 | m.insert(make_pair('4', "ghi")); 10 | m.insert(make_pair('5', "jkl")); 11 | m.insert(make_pair('6', "mno")); 12 | m.insert(make_pair('7', "pqrs")); 13 | m.insert(make_pair('8', "tuv")); 14 | m.insert(make_pair('9', "wxyz")); 15 | string tmp=""; 16 | int idx = 0; 17 | dfs(digits, m, tmp, res, idx); 18 | 19 | return res; 20 | } 21 | void dfs(string& digits, map&m, string& tmp, vector& res, int idx) { 22 | if (tmp.size() == digits.size()) { 23 | res.emplace_back(tmp); 24 | return; 25 | } 26 | for (int i = 0; i < m[digits[idx]].size(); ++i) { 27 | tmp += m[digits[idx]][i]; 28 | dfs(digits, m, tmp, res, idx + 1); 29 | tmp.pop_back(); 30 | } 31 | } 32 | }; -------------------------------------------------------------------------------- /OJ/LC-5. Longest Palindromic Substring/mirror.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | string longestPalindrome(string s) { 4 | int slen=s.size(); 5 | if (slen == 1) {return s;} 6 | int len1=0; 7 | int len2=0; 8 | int maxlen=0; 9 | int leftp=0; // left pointer 10 | int rightp=0; //right pointer 11 | for (int i=0;i rightp-leftp+1) { // update left and right pointer 16 | leftp=i-(maxlen-1)/2; 17 | rightp=i+maxlen/2; 18 | } 19 | } 20 | return s.substr(leftp,maxlen); 21 | } 22 | private: 23 | int mirror(string s, int l,int r) { // calculate the length of mirror-string 24 | int L=l; 25 | int R=r; 26 | 27 | while (L>=0 && R 3 | #include 4 | #include 5 | 6 | static const char uppertable[] = { 7 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 8 | 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 9 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 10 | 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 11 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 12 | 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 13 | 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 14 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 15 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 16 | 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 17 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 18 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 19 | 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F 20 | }; 21 | 22 | void func_toupper(char *dst, const char *src, int len) 23 | { 24 | for (int i = 0; i < len; ++i) { 25 | *dst++ = uppertable[(unsigned char)src[i]]; 26 | } 27 | *dst = 0; 28 | } -------------------------------------------------------------------------------- /graph theory/DFS/src/51. N-Queens.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> solveNQueens(int n) { 4 | vector>res; 5 | vectortmp(n, string(n,'.')); 6 | int row = 0; 7 | dfs(n, res, tmp, row); 8 | return res; 9 | } 10 | void dfs(const int& n, vector>& res, vector& tmp, int row) { 11 | if (row == n) { 12 | res.emplace_back(tmp); 13 | return; 14 | } 15 | for (int i = 0; i < n; ++i) { 16 | if (!isQvalid(tmp, row, i, n)) continue; 17 | tmp[row][i] = 'Q'; 18 | dfs(n, res, tmp, row + 1); 19 | tmp[row][i] = '.'; 20 | } 21 | } 22 | bool isQvalid(const vector& tmp, const int& row, const int& col, const int& n) { 23 | for (int i = 0; i < n; ++i) { 24 | if (tmp[i][col] == 'Q') return false; 25 | } 26 | for (int i = row - 1, j = col - 1; (i >= 0) && (j >= 0); --i, --j) { 27 | if (tmp[i][j] == 'Q') return false; 28 | } 29 | for (int i = row - 1, j = col + 1; (i >= 0) && (j < n); --i, ++j) { 30 | if (tmp[i][j] == 'Q') return false; 31 | } 32 | return true; 33 | } 34 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/93. Restore IP Addresses.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector restoreIpAddresses(string s) { 4 | vectorres; 5 | if (s.size() > 12) return res; 6 | int idx = 0; 7 | int pointnum = 0; 8 | dfs(s, res, pointnum, idx); 9 | return res; 10 | } 11 | void dfs(string& s, vector& res, int pointnum, int idx) 12 | { 13 | if (pointnum == 3) { 14 | if (isvalid(s, idx, s.size() - 1)) { 15 | res.emplace_back(s); 16 | } 17 | return; 18 | } 19 | for (int i = idx; i r) return false; 30 | if ((s[l] == '0') && (r != l)) return false; 31 | int sum = 0; 32 | for (int j = l; j <= r; ++j) { 33 | if (!isdigit(s[j])) return false; 34 | sum = sum * 10 + (s[j] - '0'); 35 | } 36 | if (sum > 255) return false; 37 | return true; 38 | } 39 | }; -------------------------------------------------------------------------------- /graph theory/DFS/src/37. Sudoku Solver.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | void solveSudoku(vector>& board) { 4 | dfs(board); 5 | } 6 | bool dfs(vector>& board) { 7 | for (int i = 0; i < 9; ++i) { 8 | for (int j = 0; j < 9; ++j) { 9 | if (board[i][j] != '.') continue; 10 | for (char val = '1'; val <= '9'; ++val) { 11 | if (isvalid(board, i, j, val)) { 12 | board[i][j] = val; 13 | if (dfs(board)) return true; 14 | board[i][j] = '.'; 15 | } 16 | } 17 | return false; 18 | } 19 | } 20 | return true; 21 | } 22 | bool isvalid(const vector>& board, const int& row, const int&col, onst char& val) { 23 | for (int i = 0; i < 9; ++i) { 24 | if (board[row][i] == val) return false; 25 | } 26 | for (int j = 0; j < 9; ++j) { 27 | if (board[j][col] == val) return false; 28 | } 29 | int startrow = row / 3 * 3; 30 | int startcol = col / 3 * 3; 31 | for (int r = startrow; r < startrow + 3; ++r) { 32 | for (int c = startcol; c < startcol + 3; ++c) { 33 | if (board[r][c] == val) return false; 34 | } 35 | } 36 | return true; 37 | } 38 | }; -------------------------------------------------------------------------------- /OJ/LC-1052. Grumpy Bookstore Owner/flp.cpp: -------------------------------------------------------------------------------- 1 | /*the first i saw this problem, as you know the key is to figure when to use the SKILL. 2 | *So i decided to calculate the biggest consecutive unsatisfied customers' number WITHOUT THE SKILL USING, so i creat one vector named tmpv which could do that. The rest is math problem. 3 | *NOTE:max_element() is great to use. 4 | */ 5 | class Solution { 6 | public: 7 | int maxSatisfied(vector& customers, vector& grumpy, int minutes) { 8 | vectortmpv; 9 | for (int i=0;i savev; 19 | savev.push_back(temp); 20 | for (int l=0,r=minutes-1;r 2 | #include 3 | 4 | #define STACK_SIZE 100 5 | 6 | typedef struct { 7 | int data[STACK_SIZE]; 8 | int top; 9 | } Stack; 10 | 11 | void init(Stack* s) { 12 | s->top = -1; 13 | } 14 | 15 | bool isEmpty(const Stack* s) { 16 | return s->top == -1; 17 | } 18 | 19 | bool isFull(const Stack* s) { 20 | return s->top == STACK_SIZE - 1; 21 | } 22 | 23 | int push(Stack* s, int item) { 24 | if (isFull(s)) { 25 | printf("stack overflow\n"); 26 | return -1; 27 | } 28 | s->data[++(s->top)] = item; 29 | printf("pushed %d into stack: ", item); 30 | for (int i = 0; i <= s->top; i++) { 31 | printf("%d ", s->data[i]); 32 | } 33 | printf("\n"); 34 | return 0; 35 | } 36 | 37 | int pop(Stack* s) { 38 | if (isEmpty(s)) { 39 | printf("stack underflow\n"); 40 | return -1; 41 | } 42 | int item = s->data[(s->top)--]; 43 | printf("popped %d from stack: ", item); 44 | for (int i = 0; i <= s->top; i++) { 45 | printf("%d ", s->data[i]); 46 | } 47 | printf("\n"); 48 | return item; 49 | } 50 | 51 | int peek(const Stack* s) { 52 | if (isEmpty(s)) { 53 | printf("stack underflow\n"); 54 | return -1; 55 | } 56 | return s->data[s->top]; 57 | } 58 | 59 | int main() { 60 | Stack s; 61 | init(&s); 62 | push(&s, 10); 63 | push(&s, 20); 64 | push(&s, 30); 65 | pop(&s); 66 | peek(&s); 67 | pop(&s); 68 | pop(&s); 69 | pop(&s); 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /string/KMP/src/kmp_java_ver.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | class Main { 4 | static char[] s, p; 5 | static int[] nxt = new int[100000]; 6 | 7 | public static void main(String[] args) { 8 | input(); 9 | buildNxt(); 10 | search(); 11 | output(); 12 | } 13 | 14 | static void search() { 15 | int tar = 0, pos = 0; 16 | 17 | while (tar < s.length) { 18 | if (s[tar] == p[pos]) { 19 | ++tar; 20 | ++pos; 21 | } else if (pos != 0) 22 | pos = nxt[pos - 1]; 23 | else 24 | ++tar; 25 | 26 | if (pos == p.length) { 27 | System.out.println(tar - pos + 1); 28 | pos = nxt[pos - 1]; 29 | } 30 | } 31 | } 32 | 33 | static void buildNxt() { 34 | int i = 1, now = 0; 35 | while (i < p.length) { 36 | if (p[i] == p[now]) { 37 | ++now; 38 | nxt[i] = now; 39 | ++i; 40 | } else if (now != 0) 41 | now = nxt[now - 1]; 42 | else { 43 | ++i; 44 | nxt[i] = now; 45 | } 46 | } 47 | } 48 | 49 | static void output() { 50 | for (int i = 0; i < p.length; ++i) 51 | System.out.printf("%d", nxt[i]); 52 | } 53 | 54 | static void input() { 55 | Scanner in = new Scanner(System.in); 56 | 57 | String str, pat; 58 | 59 | str = in.next(); 60 | pat = in.next(); 61 | 62 | s = str.toCharArray(); 63 | p = pat.toCharArray(); 64 | 65 | in.close(); 66 | } 67 | } -------------------------------------------------------------------------------- /OJ/LC-1560. Most Visited Sector in a Circular Track/map.cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector mostVisited(int n, vector& rounds) { 4 | vectorres; 5 | mapmymap; 6 | for (int i=1;i 0) { 8 | for (int j=rounds[i-1];j<=n;++j) { 9 | ++mymap[j]; 10 | } 11 | for (int k=1;k<=rounds[i];++k) { 12 | ++mymap[k]; 13 | } 14 | } 15 | else { 16 | for (int l=rounds[i-1];l<=rounds[i];++l) { 17 | ++mymap[l]; 18 | } 19 | } 20 | } 21 | for (int de=1;de>tv; //sort map and cal the maxnum 26 | sortmap(mymap,tv); 27 | auto mct=[&](int maxct)->int{ 28 | for (const auto &tvv:tv) { 29 | if (tvv.second == tv[0].second) {++maxct;} 30 | else {break;} 31 | } 32 | return maxct;}; 33 | int mn=0; 34 | int maxnum=mct(mn); 35 | 36 | for (auto it=tv.begin();itfirst); 38 | } 39 | sort(res.begin(),res.end()); 40 | return res; 41 | } 42 | private: 43 | static bool comp(const pair&left,const pair&right) { //dont forget static 44 | return left.second > right.second; 45 | } 46 | 47 | void sortmap(map&sm,vector>&sv) { 48 | for (auto curr=sm.begin();curr != sm.end();++curr) { 49 | sv.push_back(make_pair(curr->first,curr->second)); 50 | } 51 | sort(sv.begin(),sv.end(),comp); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /string/toupper/src/c_005.c: -------------------------------------------------------------------------------- 1 | /*5.int*/ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static const char uppertable[] = { 8 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 9 | 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 10 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 11 | 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 12 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 13 | 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 14 | 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 15 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 16 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 17 | 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 18 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 19 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 20 | 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F 21 | }; 22 | 23 | void func_toupper(char *dst, const char *src, int len) 24 | { 25 | int i; 26 | uint32_t eax, ebx; 27 | const uint8_t* ustr = (const uint8_t*)src; 28 | const int leftover = len % 4; 29 | const int imax = len / 4; 30 | const uint32_t* s = (const uint32_t*)src; 31 | uint32_t* d = (uint32_t*)dst; 32 | for (int i = 0; i != imax; ++i) { 33 | eax = s[i]; 34 | ebx = (0x7f7f7f7fu & eax) + 0x05050505u; 35 | ebx = (0x7f7f7f7fu & ebx) + 0x1a1a1a1au; 36 | ebx = ((ebx & ~eax) >> 2) & 0x20202020u; 37 | *d++ = eax - ebx; 38 | } 39 | 40 | i = imax * 4; 41 | dst = (char*)d; 42 | switch (leftover) { 43 | case 3: *dst++ = (char)uppertable[ustr[i++]]; 44 | case 2: *dst++ = (char)uppertable[ustr[i++]]; 45 | case 1: *dst++ = (char)uppertable[ustr[i]]; 46 | case 0: *dst = '\0'; 47 | } 48 | } -------------------------------------------------------------------------------- /others/Implement queue in C/queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAX_SIZE 5 4 | 5 | typedef struct { 6 | float data[MAX_SIZE]; 7 | int front; 8 | int rear; 9 | int size; 10 | float sum; 11 | } Queue; 12 | 13 | void init_queue(Queue* q) { 14 | q->front = 0; 15 | q->rear = 0; 16 | q->size = 0; 17 | q->sum = 0.0f; 18 | } 19 | 20 | float enqueue(Queue* q, float value) { 21 | if (q->size == MAX_SIZE) { 22 | printf("queue is full %f\n pop front element", q->data[q->front]); 23 | q->sum -= q->data[q->front]; 24 | q->front = (q->front + 1) % MAX_SIZE; 25 | q->size--; 26 | } 27 | q->data[q->rear] = value; 28 | q->rear = (q->rear + 1) % MAX_SIZE; 29 | q->size++; 30 | q->sum += value; 31 | float aveFilter = q->sum / q->size; 32 | printf("push rear element %f,queue:", value); 33 | if (q->size == 0) { 34 | // do othing 35 | } else { 36 | int i = q->front; 37 | int n = 0; 38 | while (n < q->size) { 39 | printf("%f ", q->data[i]); 40 | i = (i + 1) % MAX_SIZE; 41 | n++; 42 | } 43 | printf("\n"); 44 | } 45 | printf("aveFilter = %f\n", aveFilter); 46 | return aveFilter; 47 | } 48 | 49 | void dequeue(Queue* q) { 50 | if (q->size == 0) { 51 | return; 52 | } 53 | float value = q->data[q->front]; 54 | q->front = (q->front + 1) % MAX_SIZE; 55 | q->size--; 56 | q->sum -= value; 57 | printf("pop front element %f\n", value); 58 | return; 59 | } 60 | 61 | float queue_update(Queue* q, float value) { 62 | if (q->size == MAX_SIZE) { 63 | dequeue(q); 64 | } 65 | return enqueue(q, value); 66 | } 67 | 68 | void auto_test() { 69 | Queue q; 70 | init_queue(&q); 71 | float data[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}; 72 | for (int i = 0; i < 10; i++) { 73 | queue_update(&q, data[i]); 74 | } 75 | } 76 | 77 | int main() { 78 | auto_test(); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /OJ/OJ-69. a==b_/bythreecondition.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | string a,b; 6 | while (cin>>a>>b){ 7 | int alen=a.size(); 8 | int blen=b.size(); 9 | int tmp1=0; 10 | int tmp2=0; 11 | int tmp3=0; 12 | if (alen>blen) { 13 | for (int i=0;i 3 | #include 4 | #include 5 | #include 6 | 7 | static const char uppertable[] = { 8 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 9 | 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 10 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 11 | 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 12 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 13 | 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 14 | 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 15 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 16 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 17 | 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 18 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 19 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 20 | 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F 21 | }; 22 | 23 | void func_toupper(char *dst, const char *src, int len) 24 | { 25 | int i; 26 | uint64_t eax, ebx; 27 | const uint8_t* ustr = (const uint8_t*)src; 28 | const int leftover = len % 8; 29 | const int imax = len / 8; 30 | const uint64_t* s = (const uint64_t*)src; 31 | uint64_t* d = (uint64_t*)dst; 32 | for (int i = 0; i != imax; ++i) { 33 | eax = s[i]; 34 | ebx = (0x7f7f7f7f7f7f7f7fu & eax) + 0x0505050505050505u; 35 | ebx = (0x7f7f7f7f7f7f7f7fu & ebx) + 0x1a1a1a1a1a1a1a1au; 36 | ebx = ((ebx & ~eax) >> 2) & 0x2020202020202020u; 37 | *d++ = eax - ebx; 38 | } 39 | 40 | i = imax * 4; 41 | dst = (char*)d; 42 | switch (leftover) { 43 | case 7: *dst++ = (char)uppertable[ustr[i++]]; 44 | case 6: *dst++ = (char)uppertable[ustr[i++]]; 45 | case 5: *dst++ = (char)uppertable[ustr[i++]]; 46 | case 4: *dst++ = (char)uppertable[ustr[i++]]; 47 | case 3: *dst++ = (char)uppertable[ustr[i++]]; 48 | case 2: *dst++ = (char)uppertable[ustr[i++]]; 49 | case 1: *dst++ = (char)uppertable[ustr[i]]; 50 | case 0: *dst = '\0'; 51 | } 52 | } -------------------------------------------------------------------------------- /data structure/Disjoint Set/src/Cheese.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define MAXN 1005 4 | typedef long long ll; 5 | int fa[MAXN], rank[MAXN]; 6 | ll X[MAXN], Y[MAXN], Z[MAXN]; 7 | inline bool next_to(ll x1, ll y1, ll z1, ll x2, ll y2, ll z2, ll r) 8 | { 9 | return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2) <= 4 * r * r; 10 | // Determine whether two holes are intersecting or tangent 11 | } 12 | inline void init(int n) 13 | { 14 | for (int i = 1; i <= n; ++i) 15 | { 16 | fa[i] = i; 17 | rank[i] = 1; 18 | } 19 | } 20 | int find(int x) 21 | { 22 | return x == fa[x] ? x : (fa[x] = find(fa[x])); 23 | } 24 | inline void merge(int i, int j) 25 | { 26 | int x = find(i), y = find(j); 27 | if (rank[x] <= rank[y]) 28 | fa[x] = y; 29 | else 30 | fa[y] = x; 31 | if (rank[x] == rank[y] && x != y) 32 | rank[y]++; 33 | } 34 | int main() 35 | { 36 | int T, n, h; 37 | ll r; 38 | scanf("%d", &T); 39 | for (int I = 0; I < T; ++I) 40 | { 41 | memset(X, 0, sizeof(X)); 42 | memset(Y, 0, sizeof(Y)); 43 | memset(Z, 0, sizeof(Z)); 44 | scanf("%d%d%lld", &n, &h, &r); 45 | init(n); 46 | fa[1001] = 1001; // Use 1001 to represent the bottom 47 | fa[1002] = 1002; // Use 1002 to represent the top 48 | for (int i = 1; i <= n; ++i) 49 | scanf("%lld%lld%lld", X + i, Y + i, Z + i); 50 | for (int i = 1; i <= n; ++i) 51 | { 52 | if (Z[i] <= r) 53 | merge(i, 1001); // The hole in contact with the bottom merges with the bottom 54 | if (Z[i] + r >= h) 55 | merge(i, 1002); // The hole in contact with the top merges with the top 56 | } 57 | for (int i = 1; i <= n; ++i) 58 | { 59 | for (int j = i + 1; j <= n; ++j) 60 | { 61 | if (next_to(X[i], Y[i], Z[i], X[j], Y[j], Z[j], r)) 62 | merge(i, j); // Traverse all holes, merge intersecting or tangent balls 63 | } 64 | } 65 | printf("%s\n", find(1001) == find(1002) ? "Yes" : "No"); 66 | } 67 | return 0; 68 | } -------------------------------------------------------------------------------- /string/KMP/src/kmp_ref.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | 9 | /*递归求解next数组*/ 10 | void get_next_arr_2(const string& ss, vector& next) 11 | { 12 | next[0] = -1; // next[0]定义为-1 13 | next[1] = 0; // next[1]肯定是0 14 | int k; 15 | for (int j = 2; j < ss.size(); ++j) { 16 | k = next[j - 1]; 17 | if (k == -1) { 18 | next[j] = 0; 19 | continue; 20 | } else { 21 | while ((ss[j-1] != ss[k]) && (k != -1)) {k = next[k];} 22 | next[j] = (ss[j - 1] == ss[k]) ? (k + 1) : 0; 23 | } 24 | } 25 | } 26 | 27 | /*暴力求解next数组*/ 28 | void get_next_arr(const string& ss, vector& next) 29 | { 30 | // next[i]的求解方法是,找到从ss[0]~ss[i-1]的公共最长匹配前缀和后缀的长度 31 | next[0] = -1; // next[0]定义为-1 32 | next[1] = 0; // next[1]肯定是0 33 | // 只要待匹配串还没到底,都要求相应位的next[i]值 34 | for (int i = 2;i < ss.size(); ++i) { 35 | int max_len = i - 1; // 最长长度为i-1 36 | int len,j; 37 | for (len = max_len; len >= 1; --len) { // 从最长的情况开始搜索 38 | for (j = 0; j < len; ++j) { 39 | if(ss[j] != ss[j + i - len]) // 只要有任何一位不对应相等,那么当前len就不成立,试探下一个len 40 | break; 41 | } 42 | // 如果上一个循环是正常退出,即都对应相等了,那就把当前的len赋给next[i],并不再往下试探了 43 | if(j == len) { 44 | next[i] = len; 45 | break; 46 | } 47 | } 48 | if (len < 1) // 如果len=1的情况都不成立,那next[i]肯定是0了 49 | next[i] = 0; 50 | } 51 | } 52 | 53 | int KMP_match(const string& ms, const string& ss) 54 | { 55 | // 先求待匹配串的next集合 56 | vectornext(ss.size()); 57 | get_next_arr_2(ss, next); 58 | for_each(next.begin(),next.end(),[](int val){ 59 | cout<& price, vector>& special, vector& needs) { 4 | // if (accumulate(price.begin(),price.end(),0) == 0) { 5 | // return 0; 6 | // } 7 | this->res=INT_MAX; 8 | for (const auto& r:special) { 9 | if (avacheck(r,needs) == false) { 10 | continue; 11 | } 12 | if (inner_product(r.begin(),r.begin()+r.size()-1,price.begin(),0) < r.back()) { 13 | continue; 14 | } 15 | avaspecial.emplace_back(vector()); 16 | for (const auto& c:r) { 17 | avaspecial[avaspecial.size()-1].emplace_back(c); 18 | } 19 | } 20 | mydfs(needs,0,price); 21 | return res; 22 | } 23 | private: 24 | bool avacheck(const vector&rowspecial,const vector&checkneeds) { 25 | for (int k=0;k checkneeds[k]) { 27 | return false; 28 | } 29 | } 30 | return true; 31 | } 32 | void mydfs(vector&myneeds,int tmpsum,const vector&ctprice) { 33 | bool checkflag=false; 34 | if (tmpsum >= res) { 35 | return; 36 | } 37 | if (accumulate(myneeds.begin(),myneeds.end(),0) == 0) { 38 | res=min(res,tmpsum); 39 | return; 40 | } 41 | for (int m=0;m>avaspecial; 62 | }; 63 | -------------------------------------------------------------------------------- /string/KMP/src/kmp_demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | class KMP 9 | { 10 | public: 11 | void GetKMPNextArrInit(const string& p) { 12 | next.resize(p.size(), 0); 13 | for (int pr = p.size() - 1; pr > 0; --pr) { 14 | for (int pl = pr; pl > 0; --pl) { 15 | int i = 0; 16 | for (int pos = pl; pos <= pr; ++pos, ++i) { 17 | if (p[i] != p[pos]) { 18 | break; 19 | } 20 | } 21 | if (i == pr - pl + 1) { 22 | next[pr] = max(next[pr], i); 23 | } 24 | } 25 | } 26 | } 27 | 28 | void GetKMPNextArrQuick(const string& p) { 29 | this->next.resize(p.size(), 0); 30 | int now = 0, x = 1; 31 | while (x < p.size()) { 32 | if (p[x] == p[now]) { 33 | ++now; 34 | next[x] = now; 35 | ++x; 36 | } else if (now != 0) { 37 | now = next[now -1]; 38 | } else { 39 | next[x] = 0; 40 | ++x; 41 | 42 | } 43 | } 44 | } 45 | 46 | vector GetMasterStringStartIdx(const string& t, const string& p) { 47 | GetKMPNextArrQuick(p); 48 | int tidx = 0, pidx = 0; 49 | vectorret; 50 | 51 | while (tidx < t.size()) { 52 | if (t[tidx] == p[pidx]) { 53 | ++tidx; 54 | ++pidx; 55 | } else if (pidx != 0) { 56 | pidx = next[pidx - 1]; 57 | } else { 58 | ++tidx; 59 | } 60 | 61 | if (pidx == p.size()) { 62 | ret.emplace_back(tidx - pidx); 63 | pidx = next[pidx - 1]; 64 | } 65 | } 66 | return ret; 67 | } 68 | private: 69 | vectornext; 70 | }; 71 | 72 | int main() 73 | { 74 | string t = "abcdabdcdaabcda"; 75 | string p = "abc"; 76 | KMP mykmp; 77 | vectorv = mykmp.GetMasterStringStartIdx(t, p); 78 | for_each(v.begin(), v.end(), [&](int val) { 79 | cout << val << " "; 80 | }); cout << endl; 81 | 82 | return 0; 83 | } -------------------------------------------------------------------------------- /others/How to write if-else elegantly/README.md: -------------------------------------------------------------------------------- 1 | # How to write `if-else` elegantly 2 | 3 | To give a real example, the only modification I made was to simplify the variable names: 4 | 5 | ```c 6 | bool A = ...; 7 | bool B = ...; 8 | bool C = ...; 9 | bool D = false; 10 | 11 | if (!A) { 12 | D = true; 13 | } 14 | 15 | if ((!B) || (C == false)) { 16 | D = true; 17 | } 18 | 19 | if (D) { 20 | ...; // code snippet 1 21 | return; 22 | } 23 | 24 | if (B && (C == true)) { 25 | ...; // code snippet 2 26 | } 27 | ``` 28 | 29 | Look at this kind of code will definitely make the maintainer tremble: it is not so courageous to change it, who knows what will happen if any condition is changed. But it feels wrong to not change. Intuitively speaking, the logic should not be so complicated. So when we encounter such a logical hairball, how should we understand it? 30 | 31 | First we can combine the obvious equivalence variables and change the explicit comparisons with `true` and `false` to implicit comparisons, then it is easy to get: 32 | 33 | ```c 34 | bool A = ...; 35 | bool B = ...; 36 | bool C = ...; 37 | bool D = !A; 38 | 39 | if ((!B) || (C == false)) { 40 | D = true; 41 | } 42 | 43 | if (D) { 44 | ...; // code snippet 1 45 | return; 46 | } 47 | 48 | if (B && (C == true)) { 49 | ...; // code snippet 2 50 | } 51 | ``` 52 | 53 | We can see that `D` can do further merges: 54 | 55 | ```c 56 | bool A = ...; 57 | bool B = ...; 58 | bool C = ...; 59 | bool D = !A || (!B || !C); 60 | 61 | if (D) { 62 | ...; // code snippet 1 63 | return; 64 | } 65 | 66 | if (B && C) { 67 | ...; // code snippet 2 68 | } 69 | ``` 70 | 71 | Continue to expand `D` in the code: 72 | 73 | ```c 74 | bool A = ...; 75 | bool B = ...; 76 | bool C = ...; 77 | 78 | if (!A || (!B || !C)) { 79 | ...; // code snippet 1 80 | return; 81 | } 82 | 83 | if (B && C) { 84 | ...; // code snippet 2 85 | } 86 | ``` 87 | 88 | Doesn't the logic look a lot more succinct now? But it's not over yet, the following is the key point! Here are two formulas: 89 | 90 | 1. !(a && b) = (!a || !b) 91 | 2. !(a || b) = (!a && !b) 92 | 93 | These two formulas are absolutely magical tools. As long as you memorize them, you can safely simplify and reconstruct the messy conditions without worrying about the logical result being wrong. We transform the second `if` condition `(B && C)` with Equation 1: 94 | 95 | ```c 96 | (B && C) = !(!(B && C)) = !(!B || !C) 97 | ``` 98 | 99 | It gets interesting: 100 | 101 | ```c 102 | bool A = ...; 103 | bool B = ...; 104 | bool C = ...; 105 | 106 | if (!A || (!B || !C)) { 107 | ...; // code snippet 1 108 | return; 109 | } 110 | 111 | if (!(!B || !C)) { 112 | ...; // code snippet 2 113 | } 114 | ``` 115 | 116 | Now it is obvious that if the condition of the first `if` is false, then `(!B || !C)` must be `false`, and the second `if` becomes : 117 | 118 | ```c 119 | if (!false) { 120 | ...; // code snippet 2 121 | } 122 | ``` 123 | 124 | Obviously, the condition of the second `if` is redundant. So: 125 | 126 | ```c 127 | bool A = ...; 128 | bool B = ...; 129 | bool C = ...; 130 | 131 | if (!A || (!B || !C)) { 132 | ...; // code snippet 1 133 | return; 134 | } 135 | 136 | ...; // code snippet 2 137 | ``` 138 | 139 | Then, according to the associative law of logical algebra, the brackets in the first `if` can be removed, and the condition can be expanded: 140 | 141 | ```c 142 | bool A = ...; 143 | bool B = ...; 144 | bool C = ...; 145 | 146 | if (!A || !B || !C) { 147 | ...; // code snippet 1 148 | return; 149 | } 150 | 151 | ...; // code snippet 2 152 | ``` 153 | 154 | At this point, you will suddenly realize: そうか, it turns out that such a large pile of logic is actually the guard statement and main flow of the three conditions of `A`, `B`, and `C` . Isn’t it enough to write it like this from the beginning...? If you have forgotten what this code looks like before, I post it here for comparison: 155 | 156 | ```c 157 | bool A = ...; 158 | bool B = ...; 159 | bool C = ...; 160 | bool D = false; 161 | 162 | if (!A) { 163 | D = true; 164 | } 165 | 166 | if ((!B) || (C == false)) { 167 | D = true; 168 | } 169 | 170 | if (D) { 171 | ...; // code snippet 1 172 | return; 173 | } 174 | 175 | if (B && (C == true)) { 176 | ...; // code snippet 2 177 | } 178 | ``` 179 | 180 | The logic of these two pieces of code is actually equivalent, so even if you write `if-else`, it can be written very elegantly if you write it carefully. 181 | 182 | The more professional name of the above two magical formulas is actually **De Morgan's Law** , and the popular name is the **Law of Duality** of logical algebra. If you haven't heard of it before, I recommend that you learn about these laws of logical operations. This will make your `if-else` more professional: 183 | 184 | | NAME | FORMULA | 185 | | -------------------------------------- | ------------------------------------------------------------ | 186 | | Associative Law | a && (b && c) = (a && b) && c
a \|\| (b \|\| c) = (a \|\| b) \|\| c | 187 | | Distributive law | a \|\| (b && c) = (a \|\| b) && (a \|\| c)
a && (b \|\| c) = (a && b) \|\| (a && c) | 188 | | Duality Principle(De Morgan's Theorem) | !(a && b) = !a \|\| !b
!(a \|\| b) = !a && !b | 189 | | Absorption Law | a \|\| (a && b) = a
a && (a \|\| b) = a | 190 | 191 | -------------------------------------------------------------------------------- /string/toupper/README.md: -------------------------------------------------------------------------------- 1 | # How fast toupper is? 2 | 3 | ## Intro 4 | 5 | When coding or practicing algorithm questions, you may encounter scenarios where a string is converted to uppercase or lowercase. Different algorithms have different execution efficiencies. This article lists six implementation methods and discusses ways to improve efficiency. 6 | 7 | ## Functions 8 | 9 | For the sake of comparison effectiveness, I control the following points: 10 | 11 | 1. Each version of GCC interprets `-O3` differently, and it is rare to see applications compiled using `-O3`, so `-O3` is not tested. 12 | 2. Make sure all executable files are compiled with the same version of GCC (9.4.0) 13 | 3. The number of loops is 20,000,000: The word running time of `func_toupper` is very short, and the number obtained by `time` is close to 0. Hence the "zoom in" action. 14 | 4. The method of using `time` to obtain performance data is slightly crude, and the results are affected by various factors: other processes, interruptions, and system overhead. This also makes the result of `time` change every time. If the system is not very busy, then the error of this value is acceptable. On the contrary, this imprecise value may lead to errors in judgment. 15 | 16 | The main function and six implementation algorithms are shown below: 17 | 18 | ### main function 19 | 20 | ```c 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | extern void func_toupper(char *dst, const char *src, int len); 27 | 28 | int main(int argc, char * argv[]) 29 | { 30 | char *src = "RWAGeNIZDGsfdHOGJBAhrsLdfsKVNXesdMNBCfUEYORvxgdsdmxbmsfEEhQSfdgCDSkxDFYKTUKKDKFFsSfDFDmnVZdsfKLDSVBFPRQNKNFASRW"; 31 | 32 | char dst[256]; 33 | 34 | if (argc != 2) { 35 | printf("Usage: char_conv [times]\n"); 36 | return -1; 37 | } 38 | int times = atoi(argv[1]); 39 | for (int i = 0; i< times; ++i) { 40 | func_toupper(dst, src, strlen(src)); 41 | } 42 | 43 | (void)dst; 44 | return 0; 45 | } 46 | ``` 47 | 48 | 49 | 50 | ### c lib 51 | 52 | Call the C standard library function to traverse the conversion by characters. 53 | 54 | ```c 55 | /*1.c lib*/ 56 | #include 57 | #include 58 | #include 59 | 60 | void func_toupper(char *dst, const char *src, int len) 61 | { 62 | for (int i = 0; i < len; ++i) { 63 | *dst++ = toupper(src[i]); 64 | } 65 | *dst = 0; 66 | } 67 | ``` 68 | 69 | 70 | 71 | ### sub 72 | 73 | Subtract 32 from a lowercase ASCII character to get its corresponding uppercase character. 74 | 75 | ```c 76 | /*2.sub*/ 77 | #include 78 | #include 79 | #include 80 | 81 | void func_toupper(char *dst, const char *src, int len) 82 | { 83 | unsigned char ch; 84 | for (int i = 0; i < len; ++i) { 85 | ch = src[i]; 86 | *dst++ = (ch >= 'a' && ch <= 'z') ? (ch - 32) : ch; 87 | } 88 | *dst = 0; 89 | } 90 | ``` 91 | 92 | 93 | 94 | ### xor 95 | 96 | xor calculation with 0x20. 97 | 98 | ```c 99 | /*3.xor*/ 100 | #include 101 | #include 102 | #include 103 | 104 | void func_toupper(char *dst, const char *src, int len) 105 | { 106 | unsigned char ch; 107 | for (int i = 0; i < len; ++i) { 108 | ch = src[i]; 109 | *dst++ = (ch >= 'a' && ch <= 'z') ? ch^0x20 : ch; 110 | } 111 | *dst = 0; 112 | } 113 | ``` 114 | 115 | 116 | 117 | ### query table 118 | 119 | The ASCII table defines a total of 127 characters, which allows us to define a constant array in the same order as the ASCII table, and the values of the array correspond to their subscripts. Then, change the element value corresponding to the a-z position (0x61-0x7a) into uppercase characters. In this way, uppercase conversion becomes directly reading the value by position. 120 | 121 | ```c 122 | /*4.query table*/ 123 | #include 124 | #include 125 | #include 126 | 127 | static const char uppertable[] = { 128 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 129 | 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 130 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 131 | 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 132 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 133 | 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 134 | 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 135 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 136 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 137 | 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 138 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 139 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 140 | 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F 141 | }; 142 | 143 | void func_toupper(char *dst, const char *src, int len) 144 | { 145 | for (int i = 0; i < len; ++i) { 146 | *dst++ = uppertable[(unsigned char)src[i]]; 147 | } 148 | *dst = 0; 149 | } 150 | ``` 151 | 152 | 153 | 154 | ### int 155 | 156 | Take advantage of CPU Cache capabilities. Take the string modulo 4 and calculate the remainder according to the table lookup method. 157 | 158 | ```c 159 | /*5.int*/ 160 | #include 161 | #include 162 | #include 163 | #include 164 | 165 | static const char uppertable[] = { 166 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 167 | 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 168 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 169 | 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 170 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 171 | 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 172 | 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 173 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 174 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 175 | 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 176 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 177 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 178 | 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F 179 | }; 180 | 181 | void func_toupper(char *dst, const char *src, int len) 182 | { 183 | int i; 184 | uint32_t eax, ebx; 185 | const uint8_t* ustr = (const uint8_t*)src; 186 | const int leftover = len % 4; 187 | const int imax = len / 4; 188 | const uint32_t* s = (const uint32_t*)src; 189 | uint32_t* d = (uint32_t*)dst; 190 | for (int i = 0; i != imax; ++i) { 191 | eax = s[i]; 192 | // http://azo;;ionmonkey.com/qed/asmexample.html 193 | ebx = (0x7f7f7f7fu & eax) + 0x05050505u; 194 | ebx = (0x7f7f7f7fu & ebx) + 0x1a1a1a1au; 195 | ebx = ((ebx & ~eax) >> 2) & 0x20202020u; 196 | *d++ = eax - ebx; 197 | } 198 | 199 | i = imax * 4; 200 | dst = (char*)d; 201 | switch (leftover) { 202 | case 3: *dst++ = (char)uppertable[ustr[i++]]; 203 | case 2: *dst++ = (char)uppertable[ustr[i++]]; 204 | case 1: *dst++ = (char)uppertable[ustr[i]]; 205 | case 0: *dst = '\0'; 206 | } 207 | } 208 | ``` 209 | 210 | 211 | 212 | ### int64 213 | 214 | For the currently used CPU, its cache_line is 64 byte, so method 5 is further revised to the cache_line method. 215 | 216 | ```c 217 | /*6.int64*/ 218 | #include 219 | #include 220 | #include 221 | #include 222 | 223 | static const char uppertable[] = { 224 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 225 | 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 226 | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 227 | 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 228 | 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 229 | 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 230 | 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 231 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 232 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 233 | 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 234 | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 235 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 236 | 0x5A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F 237 | }; 238 | 239 | void func_toupper(char *dst, const char *src, int len) 240 | { 241 | int i; 242 | uint64_t eax, ebx; 243 | const uint8_t* ustr = (const uint8_t*)src; 244 | const int leftover = len % 8; 245 | const int imax = len / 8; 246 | const uint64_t* s = (const uint64_t*)src; 247 | uint64_t* d = (uint64_t*)dst; 248 | for (int i = 0; i != imax; ++i) { 249 | eax = s[i]; 250 | // http://azo;;ionmonkey.com/qed/asmexample.html 251 | ebx = (0x7f7f7f7f7f7f7f7fu & eax) + 0x0505050505050505u; 252 | ebx = (0x7f7f7f7f7f7f7f7fu & ebx) + 0x1a1a1a1a1a1a1a1au; 253 | ebx = ((ebx & ~eax) >> 2) & 0x2020202020202020u; 254 | *d++ = eax - ebx; 255 | } 256 | 257 | i = imax * 4; 258 | dst = (char*)d; 259 | switch (leftover) { 260 | case 7: *dst++ = (char)uppertable[ustr[i++]]; 261 | case 6: *dst++ = (char)uppertable[ustr[i++]]; 262 | case 5: *dst++ = (char)uppertable[ustr[i++]]; 263 | case 4: *dst++ = (char)uppertable[ustr[i++]]; 264 | case 3: *dst++ = (char)uppertable[ustr[i++]]; 265 | case 2: *dst++ = (char)uppertable[ustr[i++]]; 266 | case 1: *dst++ = (char)uppertable[ustr[i]]; 267 | case 0: *dst = '\0'; 268 | } 269 | } 270 | ``` 271 | 272 | 273 | 274 | ## Result 275 | 276 | All results below are in seconds. 277 | 278 | | Function | -O0 | -O1 | -O2 | 279 | | :---------- | :---: | :---: | :---: | 280 | | c function | 18.888 | 5.219 | 4.939 | 281 | | sub | 18.173 | 5.516 | 5.842 | 282 | | xor | 18.140 | 6.362 | 6.291 | 283 | | query table | 14.080 | 3.950 | 4.711 | 284 | | int | 7.218 | 2.601 | 2.441 | 285 | | int64 | 4.073 | 1.530 | 1.767 | 286 | 287 | The trend of the 3 sets of data is the same. For this small program, the optimization effects of `-O1` and `-O2` are the same, and the running time of each version of the use case is very close. 288 | 289 | We then use the `-ftree-vectorize` option in GCC to optimize execution through automatic vectorization. Compile the above 6 methods again and get the following comparative test results: 290 | 291 | | Function | -O2 | -O2 -ftree-vectorize | 292 | | :---------- | :---: | :------------------: | 293 | | c function | 4.939 | 5.092 | 294 | | sub | 5.842 | 0.696 | 295 | | xor | 6.291 | 0.716 | 296 | | query table | 4.711 | 4.463 | 297 | | int | 2.441 | 0.834 | 298 | | int64 | 1.767 | 0.748 | 299 | 300 | It can be seen that the vectorization version has the greatest benefit in the subtraction and xor operation versions, directly beating all previous versions. -------------------------------------------------------------------------------- /string/KMP/README.md: -------------------------------------------------------------------------------- 1 | # KMP 2 | 3 | ## Exact matching: what's the problem? 4 | 5 | Given a string `P` called the pattern and a longer string `T` called the text, the exact matching problem is to find all occurrences, if any, of pattern `P` in text `T`. For example, if `P` = `aba` and `T` = `bbabaxababay` then `P` occurs in `T` starting at locations `3`, `7`, and `9`. Note that two occurrences of `P` may overlap, as illustrated by the occurrences of `P` at locations `7` and `9`. —— via *Algorithms on Strings, Trees, and Sequences* 6 | 7 | ## Introduce 8 | 9 | [wikipedia](https://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm) 10 | 11 | I supposed that you have read this wiki for times... 12 | 13 | ## Brute-Force in 14 | 15 | Before learning KMP, BM, RK and other algorithms like these, let's start with the naive method - Brute-Force. 16 | ### Implement 17 | 18 | Now that we know what the string matching problem is, the simple brute force algorithm should be ready to come out. BF algorithm is both clear and simple in thinking and logic. 19 | 20 | * enumerate `i` = `0,1,2,3,...,len(T) - len(P)`. 21 | * Compare whether `T[i : i + len(P)]` is equal to `P`, and if they are consistent, one match found. 22 | 23 | ``` 24 | //CPP 25 | void bfStringMatch(string p, string t) 26 | { 27 | int plen = p.size(); 28 | int tlen = t.size(); 29 | if (plen > tlen) { 30 | return; 31 | } 32 | for (int i = 0; i <= tlen - plen; ++i) { 33 | bool flag = true; 34 | for (int j = 0; j < plen; ++j) { 35 | if (p[j] ! = t[i + j]) { 36 | flag = false; 37 | break; 38 | } 39 | } 40 | if (flag) { 41 | cout << i << " "; 42 | } 43 | } 44 | } 45 | ``` 46 | 47 | ### Complexity of Brute Force 48 | 49 | 1. Single small task of "string comparison": the worst possible situation is the following - the only difference between the two strings is the last character. In this case, the pattern string must go through the complete matching string to be able to come to a result, the complexity is $O(len)$. 50 | 2. The worst case that the brute force algorithm may encounter: assume $n = len(P)$, $m = len(T)$. Each small task pays $n$ character comparison costs, and a total of $m - n + 1$ comparisons are required, so the time complexity is $O(n*(m-n+1))$. Considering that the text string is generally much longer than the pattern string, the complexity of the brute force algorithm is $O(nm)$. 51 | 52 | ## Improvements to the Brute Force 53 | 54 | Although the code logic is simple, it is not difficult to find that the brute force algorithm is as slow as crawling. Its worst case is shown in the figure below: 55 | 56 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/BF.png) 57 | 58 | It is difficult for us to reduce the complexity of "small tasks" (because comparing two strings of equal length, you can only compare characters one by one). Therefore, we consider reducing the number of "small tasks". If the number of comparisons can be reduced enough, the overall complexity will also reduce a lot. 59 | 60 | To optimize an algorithm, the first question to answer is **"What information do I have?"** Whether the information we have is sufficient and effective determines how far we can optimize the algorithm. **Please remember: It is the idea of the KMP algorithm to make use of the residual information as much as possible. ** 61 | 62 | In the brute force algorithm, if the comparison from `T[i]` fails, the algorithm will directly start to try to compare from `T[i+1]`. This kind of behavior is a typical **"not learning from previous mistakes"**. We should note that a failed match will provide us with valuable information - if the match of `T[i : i+len(P)]` fails at the position of `r`, then starting from `T[i]`, there must be `( r-1)` consecutive characters exactly the same as the first `(r-1)` characters of `P`! 63 | 64 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/prefix.png) 65 | 66 | The task that needs to be realized is "string matching", and every failure will give us some information in exchange - it can tell us that a certain substring of the text string is equal to a certain prefix of the pattern string. But what's the use? 67 | 68 | ### Skip impossible string comparisons 69 | 70 | Some "small tasks" of string comparisons are likely to succeed, and some are not. We just mentioned that the goal of optimizing Brute-Force is to "minimize the number of 'small tasks'", and if we skip those "small tasks" that are never likely to succeed, we can hope that the complexity will be reduced to an acceptable range. 71 | 72 | So, which "small tasks" are impossible to succeed? Let's look at an example. The known information is as follows: 73 | 74 | * Pattern string `P` = `"abcabd"`. 75 | * When matching the text string starting from `T[0]`, there is a mismatch at `P[5]`. 76 | 77 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/1st_time_match.png) 78 | 79 | First, use the conclusions from the previous section. Since it is mismatched at `P[5]`, it means that `T[0:5]` is equal to `P[0:5]`, which is `"abcab"`. Now let's consider: from `T[1]`, `T[2]`, `T[3]` The initial match attempt, is there any chance of success? 80 | 81 | Starting from `T[1]` will definitely not succeed, because `T[1]` = `P[1]` = '`b'`, which is not equal to `P[0]`. Starting from `T[2]` is also useless, because `T[2]` = `P[2]` = `'c'`, which is not equal to `P[0]`. But starting from `T[3]` is possible - at least according to the established known information, we can not deduce the contradiction. 82 | 83 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/move2try.png) 84 | 85 | With the idea of **"skip impossible attempts"**, we introduce the next array. 86 | 87 | ## next array 88 | 89 | The next array is for the pattern string. The next array of `P` is defined as: `next[i]` represents a substring of `P[0]` ~ `P[i]`, that **the first k characters** are exactly equal to the largest `k` of the **last k characters**. In particular, `k` cannot take `i+1` (because this substring has only `i+1` characters in total, and it must be equal to itself, so it is meaningless). 90 | 91 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/next_intro.png) 92 | 93 | The figure above gives an example. When `P`=`"abcabd"`, `next[4]`=`2`, because the substring `P[0]` ~ `P[4]` is `"abcab"`, the first two characters are equal to the last two characters, so `next[4]` take 2. And `next[5]`=`0`, because `"abcabd"` can't find the same prefix and suffix, so it can only take `0`. 94 | 95 | If the pattern string is regarded as a **ruler** and moves on the text string, then Brute-Force only shifts `1` to the right after each mismatch; the improved algorithm is to move many indexes after each mismatch, skipping those that are not possible locations for a successful match. But how do you determine how many indexes to shift? 96 | 97 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/how2movebynext.png) 98 | 99 | After `S[0]` tries to match, and the mismatch is `S[3]` <=> `P[3]`, we directly shift the pattern string to the right by `2`, so that `S[3]` is aligned with `P[1]`. Then continue matching, the mismatch is `S[8]` <=> `P[6]`, then we shift `P` to the right by `3`, and align `S[8]` with `P[3]`. Then continue to match until it succeeds. 100 | 101 | How should we move this ruler? Obviously, as shown by the arrow in the figure, the old suffix must be consistent with the new prefix (if not, it will definitely not match)!j 102 | 103 | Recall the property of the next array: in the substring from `P[0]` to `P[i]`, the first `next[i]` characters are exactly the same as the last `next[i]` characters. That being the case, if the mismatch is in `P[r]`, then in the section `P[0]`~`P[r-1]`, the first `next[r-1]` characters are exactly equal to the last `next[r-1]` characters - That is to say, we can use the prefix whose length is `next[r-1]` to replace the position of the current suffix and let the matching continue! 104 | 105 | You can verify the above matching example: after `P[3]` is mismatched, align `P[next[3-1]]`(`P[1]`), with the index that the text string just mismatched; After `P[6]` is mismatched, align `P[next[6-1]]`(`P[3]`), with the index that the text string just mismatched. 106 | 107 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/ruler_move.png) 108 | 109 | ### The create and use of next array 110 | 111 | After understanding the principle of using the next array to speed up string matching, let's implement it in the next code. I divided it into two parts: 112 | 113 | 1. Create next array 114 | 2. Use the next array for matching 115 | 116 | The first is to create the next array. Let's use the simplest method first, and come back to optimize it later: 117 | 118 | ``` 119 | //CPP 120 | void GetKMPNextArrInit(const string& p) { 121 | next.resize(p.size(), 0); 122 | for (int pr = p.size() - 1; pr > 0; --pr) { 123 | for (int pl = pr; pl > 0; --pl) { 124 | int i = 0; 125 | for (int pos = pl; pos <= pr; ++pos, ++i) { 126 | if (p[i] != p[pos]) { 127 | break; 128 | } 129 | } 130 | if (i == pr - pl + 1) { 131 | next[pr] = max(next[pr], i); 132 | } 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | You might be furious because of triple-for loop used. "Damn, the reason why I learn KMP is to not use such many fors". However, python can be more concise: 139 | 140 | ``` 141 | #python 142 | def getNext(x): 143 | for i in range(x, 0, -1): 144 | if p[0:i] == p[x-i+1:x+1]: 145 | return i 146 | return 0 147 | 148 | next = [getNext(x) for x in range(len(p))] 149 | ``` 150 | 151 | As shown in the code above, the next array is created directly according to the definition of the next array. It is not difficult to find that its complexity is $O(n^2)$. 152 | 153 | Next, realize the use of the next array to speed up string matching. code show as below: 154 | 155 | ``` 156 | vector GetMasterStringStartIdx(const string& t, const string& p) { 157 | GetKMPNextArr(p); 158 | int tidx = 0, pidx = 0; 159 | vectorret; 160 | while (tidx < t.size()) { 161 | if (t[tidx] == p[pidx]) { 162 | ++tidx; 163 | ++pidx; 164 | } else if (pidx != 0) { 165 | pidx = next[pidx - 1]; 166 | } else { 167 | ++tidx; 168 | } 169 | if (pidx == p.size()) { 170 | ret.emplace_back(tidx - pidx); 171 | pidx = next[pidx - 1]; 172 | } 173 | } 174 | return ret; 175 | } 176 | ``` 177 | 178 | How to analyze the complexity of this string matching? At first glance, the value of `pidx` may keep changing to `next[pidx-1]`, and the cost will be very high; but we use the amortized analysis, it is clear that the value of `pidx` will increase at most $len(S)$ times, so the value of `pidx` will decrease The number of times will not be higher than $len(T)$ times. Therefore, the complexity is acceptable, and it is not difficult to analyze the time complexity of the entire matching algorithm: $O(m+n)$. 179 | 180 | ### Quickly build the next array 181 | 182 | Finally we come to our last question - how to quickly build the next array. 183 | 184 | First: Quickly constructing the next array is the essence of the KMP algorithm. The core idea is **"P matches itself with itself"**. 185 | 186 | Why do I say this way? Review the complete definition of the next array: 187 | 188 | * Define "k-prefix" as the first number of `k` characters of a string; "k-suffix" as the last number of `k` characters of a string. `k` must be less than the string length. 189 | 190 | * `next[x]` is defined as: a string of `P[0]`~`P[x]`, to make the largest `k` by **k-prefix equal to k-suffix**. 191 | 192 | In this definition, one match is implicitly included—prefix and suffix are equal. Next, we consider recursively finding the next array. If `next[0]`, `next[1]`,..., `next[x-1]` are all known, how to find `next[x]`? 193 | 194 | Let's discuss the situation. First of all, we already know `next[x-1]` (hereinafter referred to as `now`), if `P[x]` is the same as `P[now]`, then the length of the longest equal prefix and suffix can be extended by one, obviously `next[x]` = `now` + `1`. The diagram is as follows. 195 | 196 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/quick_solve_next.png) 197 | 198 | Just solved the case where P[x] = P[now] . What if P[x] is different with P[now]? 199 | 200 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/x!%3Dnow.png) 201 | 202 | As shown in the picture. The substring `A` and `B` of length `now` are the longest common prefix and suffix in `P[0]`~`P[x-1]`. Unfortunately, the character on the right of `A` is not equal to the character on the right of `B`, so `next[x]` cannot be changed to `now+1`. Therefore, we should **shorten this `now`**, change it to a smaller value, and try whether `P[x]` is equal to `P[now]`. 203 | 204 | How much should `now` be reduced to? Obviously, we don't want to shrink the `now` too much. Therefore, we decided to make this new `now` as large as possible while maintaining "the now-prefix of `P[0]` ~ `P[x-1]` is still equal to the now-suffix". For the public prefix and suffix of `P[0]` ~ `P[x-1]`, the prefix must fall in string `A`, and the suffix must fall in string `B`. In other words: the next `now` should be changed to: make the largest `k` by **A's k-prefix** equal to **B's k-suffix**. 205 | 206 | You should have noticed a very strong property - **string A and string B are the same!** `B`'s suffix is equal to` A`'s suffix! Therefore, the largest `k` that makes the k-prefix of `A` equal to the k-suffix of `B` is actually the length of the longest common prefix and suffix of string `A` - `next[now-1]`! 207 | 208 | ![image](https://github.com/ryan4waters/algo/blob/main/string/KMP/figures/quick_final.png) 209 | 210 | Look at the example above. When `P[now]` is not equal to `P[x]`, we need to shrink now - turn `now` into `next[now-1]` until `P[now]`=`P[x]`. When `P[now]`=`P[x]`, you can directly expand to the right. 211 | 212 | The CPP code is as follows: 213 | 214 | ``` 215 | void GetKMPNextArrQuick(const string& p) { 216 | this->next.resize(p.size(), 0); 217 | int now = 0, x = 1; 218 | while (x < p.size()) { 219 | if (p[x] == p[now]) { 220 | ++now; 221 | next[x] = now; 222 | ++x; 223 | } else if (now != 0) { 224 | now = next[now -1]; 225 | } else { 226 | next[x] = 0; 227 | ++x; 228 | } 229 | } 230 | } 231 | ``` 232 | 233 | It is not difficult to prove that the time complexity of constructing the next array is $O(m)$. So far, with the time complexity of $O(m+n)$, we have realized building the next array and using the next array for string matching. 234 | -------------------------------------------------------------------------------- /graph theory/DFS/README.md: -------------------------------------------------------------------------------- 1 | # DFS 2 | 3 | ## Problem list 4 | 5 | * Subset 6 | * [78. Subsets](https://leetcode.com/problems/subsets/) 7 | * [90. Subsets II](https://leetcode.com/problems/subsets-ii/) 8 | 9 | * Combination 10 | * [77. Combinations](https://leetcode.com/problems/combinations/) 11 | * [17. Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/) 12 | * [39. Combination Sum](https://leetcode.com/problems/combination-sum/) 13 | * [40. Combination Sum II](https://leetcode.com/problems/combination-sum-ii/) 14 | * [216. Combination Sum III](https://leetcode.com/problems/combination-sum-iii/) 15 | 16 | * Permutation 17 | * [46. Permutations](https://leetcode.com/problems/permutations/) 18 | * [47. Permutations II](https://leetcode.com/problems/permutations-ii/) 19 | 20 | * Cutting 21 | * [131. Palindrome Partitioning](https://leetcode.com/problems/palindrome-partitioning/) 22 | * [93. Restore IP Addresses](https://leetcode.com/problems/restore-ip-addresses/) 23 | 24 | * Checkerboard 25 | * [51. N-Queens](https://leetcode.com/problems/n-queens/) 26 | * [37. Sudoku Solver](https://leetcode.com/problems/sudoku-solver/) 27 | 28 | * Island 29 | * [200. Number of Islands](https://leetcode.com/problems/number-of-islands/) 30 | * [1254. Number of Closed Islands](https://leetcode.com/problems/number-of-closed-islands/) 31 | * [1020. Number of Enclaves](https://leetcode.com/problems/number-of-enclaves/) 32 | * [695. Max Area of Island](https://leetcode.com/problems/max-area-of-island/) 33 | * [1905. Count Sub Islands](https://leetcode.com/problems/count-sub-islands/) 34 | * [694. Number of Distinct Islands](https://leetcode.com/problems/number-of-distinct-islands/) 35 | 36 | * Others 37 | * [491. Non-decreasing Subsequences](https://leetcode.com/problems/non-decreasing-subsequences/) 38 | * [332. Reconstruct Itinerary](https://leetcode.com/problems/reconstruct-itinerary/) 39 | 40 | ## Subset 41 | 42 | The subset problem can be divided into two categories based on whether the elements of the target array have duplicate parts: 43 | 44 | 1. The elements of the target array are completely different 45 | 2. the target array is partially duplicated 46 | 47 | The pruning of the subset problem is a de-duplication operation for case 2, and there are three ways to de-duplicate (note that first sorting of the candidate array). 48 | 49 | 1. `set` 50 | 51 | ``` 52 | unordered_set uset; 53 | for (int i = startidx; i < nums.size(); ++i) { 54 | if (uset.find(nums[i]) != uset.end()) continue; 55 | uset.insert(nums[i]); 56 | tmp.push_back(nums[i]); 57 | dfs(nums, i + 1); 58 | tmp.pop_back(); 59 | } 60 | ``` 61 | 62 | 2. `used` array 63 | 64 | ``` 65 | for (int i = startidx; i < nums.size(); ++i) { 66 | if ((i > 0) && (nums[i] == nums[i - 1]) && (used[i - 1] == false)) continue; 67 | tmp.push_back(nums[i]); 68 | used[i] = true; 69 | dfs(nums, i + 1, used); 70 | used[i] = false; 71 | tmp.pop_back(); 72 | } 73 | ``` 74 | 75 | 3. `if` (without `used` array) 76 | 77 | ``` 78 | for (int i = startidx; i < nums.size(); ++i) { 79 | if ((i > startidx) && (nums[i] == nums[i - 1])) { 80 | continue; 81 | } 82 | tmp.push_back(nums[i]); 83 | dfs(nums, i + 1); 84 | tmp.pop_back(); 85 | } 86 | ``` 87 | 88 | ## Combination 89 | 90 | Similar to the subset problem, the `candidates` array of the combination problem can be divided into two categories with/without duplicate elements, but the combination problem also considers whether the `k` value (the size of the subarray `tmp`) is given and whether the `candidates` array elements can be reused, etc. 91 | 92 | * For `candidates` array elements whether there are duplicates and how many times they can be used 93 | When the `candidates` array contains duplicate elements (Each element of the `candidates` array can only be used a maximum of 1 time by the `tmp` array, otherwise the duplication of the elements of the `candidates` array itself is meaningless), you must use `sort + de-duplication`, in this case, the de-duplication operation is the same as in the subset problem. And when the `tmp` array can use any element of the `candidates` array multiple times, then use `sort + prune i`. 94 | 95 | * For `startidx` and `i`. 96 | Combination problem for path exploration at a node, **start position `startidx` (generally `startidx`, `startidx` is required for single array combinations, but not required for multi-array combinations, see [17. Letter combinations](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)) == recursively pass `startidx == i + 1` (any element is used at most once) or `i` (any element is used an unlimited number of times, and `i` is pruned at the end of the loop condition)**. Specifically, when the `candidates` array does not contain duplicate elements and the `tmp` array has no limit on the number of times the elements of the candidates array can be used, the `startidx` passed in recursively should be `i` so that the loop starts from `startidx` after entering recursion, not from `0`, for the following reasons. 97 | 98 | ![image](https://github.com/ryan4waters/algo/blob/main/graph%20theory/DFS/figures/startidx.png) 99 | 100 | The case where any element is used at most once (`startidx == i + 1`) is not repeated, for the same reason. 101 | 102 | * For K. 103 | The value of K is given or not directly affects pruning (the for-loop termination condition, i.e. the range of `i`): 104 |
; i < candidates.size() - k + tmp.size() + 1;
105 | 106 | But pruning of the i range operation does not affect the end condition. If [39. Combination Sum](https://leetcode.com/problems/combination-sum/) and [40. Combination Sum II](https://leetcode.com/problems/combination-sum-ii/) add the constraint `k`, don't forget to fix the end condition. 107 | 108 | ## Permutation 109 | 110 | Similarly, permutation problems can be divided into two categories based on whether the elements within thecandidates array are repeated or not, but differ from combination problems with the following characteristics. 111 | 112 | * Each level is searched from `0` instead of `startidx` 113 | * need `used` array to record which elements have been put in the `tmp` array 114 | 115 | *talk is cheap:* 116 | 117 | For the array of non-duplicate elements `candidates` 118 | 119 | ``` 120 | for (int i = 0; i < candidates,size(); ++i) { 121 | if (used[i]) continue; 122 | tmp.emplace_back(candidates[i]); 123 | used[i] = true; 124 | dfs(…); 125 | used[i] = false; 126 | tmp.pop_back(); 127 | } 128 | ``` 129 | 130 | For the array of duplicate elements `candidates`. 131 | 132 | ``` 133 | sort(); 134 | for() { 135 | if ((i > 0) && (candidates[i] == candidates[i-1]) && (used[i-1] == false) continue; 136 | if (!used[i]) { 137 | tmp.emplace_back(candidates[i]); 138 | used[i] = true; 139 | dfs(); 140 | used[i] = false; 141 | tmp.pop_back(); 142 | } 143 | } 144 | ``` 145 | 146 | ## Cutting 147 | 148 | The cutting problem deals with strings in general. The cutting problem has two difficulties, the first is to abstract it into a combination problem, and the second is the understanding of the cutting interval division(`startidx` and `i`). Once the abstraction and interval division are done, it is not difficult to write the following recursive backtracking code framework. 149 | 150 | ``` 151 | dfs(…,idx) { 152 | …//Leaf node: end condition + result collection 153 | for(int i = idx; i < s.size(); ++i){ 154 | if (!isValid(a, startidx, i)) continue; // pruning operations, determine whether the pre-order substring [startidx,i] satisfies the condition 155 | … // pre-order 156 | dfs(…, i + 1); 157 | … // post-order 158 | } 159 | } 160 | ``` 161 | 162 | The cutting conditions given in the problems can be transformed into pruning operations (palindromes, ip, etc.). The introduction of judgments or operations on strings often weakens the difficulty of the problems, and specific code implementations are not displayed here. 163 | 164 | ## Checkerboard 165 | 166 | ### N Queens 167 | 168 | The search for the entire board is performed from left to right and from top to bottom. The downward traversal of nodes all starts at `0` and stops at `N-1`, i.e. `i∈[0,N)`. The depth of the recursion, i.e. `startidx`, is also `[0,N)`. After confirming the width and depth of the traversal, the dfs entry can also be confirmed, that is: `chessbox + N + startidx`. 169 | 170 | Still remember how the cutting problem is pruned? Yes, similar to the cut problem, the N queen problem is also based on the conditions of the node preorder traversal to pruning, and different with cutting problem: the cutting problem pruning judgment is only for the preorder substring itself, while the N queen pruning is required to observe the path. 171 | 172 | It is not difficult to write the following core code. 173 | 174 | ``` 175 | dfs(){ 176 | if (startidx == N) { 177 | res.emplace_back(chessbox); 178 | return; 179 | } 180 | 181 | for (int i = 0; i < N; ++i) { 182 | if (!isValid()) continue; // pruning operations, determine whether the pre-order position [startidx,i] satisfies the condition 183 | chessbox[startidx, i] = ’Q’; 184 | dfs(…, startidx + 1); 185 | chessbox[startidx][i] = ’.’; 186 | } 187 | } 188 | ``` 189 | 190 | Pruning implementation. 191 | 192 | ``` 193 | bool isValid(){ 194 | for (){if () return false;} // rows 195 | for (){if () return false;} // column 196 | for (){if () return false;} // top left corner 197 | for (){if () return false;} // top right corner 198 | return true; 199 | } 200 | ``` 201 | 202 | ### Sudoku Solver 203 | 204 | After solving the N Queens problem, can we write the answer to Sudoku Solver? Listed here are the two ways of recursion by row traversal and two-dimensional recursion (row + column). 205 | 206 | #### Core code 207 | 208 | 1. Iteration by rows 209 | 210 | ``` 211 | bool dfs(vector>& board, int i, int j) { 212 | if (j == 9) { 213 | return dfs(board, i + 1, 0); 214 | } 215 | if (i == 9) {return true;} 216 | if (board[i][j] != '.') { 217 | return dfs(board, i, j + 1); 218 | } 219 | for (char val = '1'; val <= '9'; ++val) { 220 | if (!isValid(board, i, j, val)) continue; 221 | board[i][j] = val; 222 | if(dfs(board, i, j + 1)) return true; 223 | board[i][j] = '.'; 224 | } 225 | return false; 226 | } 227 | ``` 228 | 229 | 2. 2-D deep search 230 | 231 | ``` 232 | bool dfs() { 233 | for (int i = 0; i < 9; ++i) { // row 234 | for (int j = 0; j <9; ++j) { // colunm 235 | if (board[i][j] != '.') continue; 236 | for (char val = '1'; val <= '9'; ++val) { // determine whether [i, j] could write down `val` 237 | if (isValid(i, j, val, board)) { 238 | board[i][j] = val; // write down `val` 239 | if (dfs(board)) return true; // return immediately 240 | board[i][j] = '.'; // backtrace,erase `val` 241 | } 242 | } 243 | return false; // all fail 244 | } 245 | } 246 | return true; 247 | } 248 | ``` 249 | 250 | #### pruning 251 | 252 | * plain pruning: 253 | 254 | ``` 255 | bool isValid(){ 256 | for (int i = 0; i < 9; i) {if (board[row][i] == val) return false;} // row 257 | for (int j = 0; j < 9; j) {if (board[j][col] == val) return false;} // column 258 | for (int sr = row / 3 * 3; sr< row / 3 * 3 + 3; ++sr) { // 3x3 blocks 259 | for (int sc = col / 3 * 3; sc < col / 3 * 3 + 3; ++sc) { 260 | if(board[sr][sc] == val) return false; 261 | } 262 | } 263 | return true; 264 | } 265 | ``` 266 | 267 | * Optimized pruning: 268 | 269 | ``` 270 | bool isValid(){ 271 | for (int i = 0; i < 9; ++i) { 272 | if (board[row][i] == val) return false; 273 | if(board[i][col] == val) return false; 274 | if(board[row][i] == val) return false; 275 | if(board[row/3*3+i/3][col/3*3+i%3] == val) return false; 276 | } 277 | return true; 278 | } 279 | ``` 280 | 281 | ## Island 282 | 283 | The island problem is mainly to solve 1) number and 2) area problems, and one of the most important ideas to face the island problem is the "flooding idea". The emergence of flooding can eliminate the maintenance of `used` arrays. 284 | 285 | ### Number of Islands 286 | 287 | Introductory island problem, write the framework of this problem 288 | 289 | ``` 290 | int solve() { 291 | for () { // row 292 | for() { // column 293 | if (grid[i][j] == 0) continue; 294 | ++res; 295 | dfs(grid, i, j); 296 | } 297 | } 298 | } 299 | 300 | int dfs(grid, row, col) { // "flooding idea" 301 | if (…) return; // out of range 302 | if (grid[row][col] == 0) return; 303 | grid[row][col] = 0; 304 | dfs(); 305 | dfs(); 306 | dfs(); 307 | dfs(); 308 | } 309 | ``` 310 | 311 | ### Number of Closed Islands 312 | 313 | Compared to the previous problem, it's easy to see that the islands that were on the boundary are not counted, so it's straightforward to build on the previous problem by flooding the islands on the boundary first. If you understand the code of the previous problem, you will see that the flooding recursion is to flood a small area of islands, so we need to use the same recursion to "solve" the islands at the boundary, killing two birds with one stone. 314 | 315 | ``` 316 | int solve() { 317 | for () { // up and down 318 | dfs(, 0, i); 319 | dfs(, grid.size() - 1, i); 320 | } 321 | for() { // left-right 322 | dfs(, j, 0); 323 | dfs(, j, grid[0].size()); 324 | } 325 | … 326 | } 327 | ``` 328 | 329 | ### Number of Enclaves 330 | 331 | After understanding the problem, you will find that this problem is to find the area of all closed islands. To find the closed islands, you should first think of "removing the edges", and then think about how to calculate the area. Yes, after removing the edges, the sum of the remaining land is the enclave, isn't it? That's right, it is the total number of land in the two-dimensional array after iterating through "removing the edges" operation. The exact code is not displayed here. 332 | 333 | 334 | ### Max Area of Island 335 | 336 | After finding the sum of the area of closed islands, how do you find the maximum area of an island? If you remember what was said above about "flooding all of a small island", then calculating the amount of land on a single island is not a problem. 337 | 338 | ``` 339 | dfs() { 340 | for() { 341 | for() { 342 | if (grid[i][j] == 0) continue; 343 | singleislandcover=dfs(grid, i, j); 344 | res=max(res, singleislandcover); 345 | } 346 | } 347 | } 348 | 349 | int dfs() { 350 | if (…) return 0; 351 | grid[r][c] = 0; 352 | return dfs() + dfs() + dfs() + dfs() + 1; 353 | } 354 | ``` 355 | 356 | ## Others 357 | 358 | To be continued... 359 | -------------------------------------------------------------------------------- /data structure/Disjoint Set/README.md: -------------------------------------------------------------------------------- 1 | # Disjoint Set 2 | 3 | Disjoint Set is considered by many OIers to be one of the most concise and elegant data structures, mainly used to solve the problem of elements-grouping. It manages a series of disjoint sets and supports two operations: 4 | 5 | * Union: Merge two disjoint sets into one set. 6 | * Find: Find whether two elements are in the same set. 7 | 8 | Of course, such a definition is too academic, and I am afraid that unable to understand its specific use after reading it. So let's take a look at the most direct application scenario of Disjoint Set: [Relative Problem](https://www.luogu.com.cn/problem/P1551). 9 | 10 | This is actually a very practical question. We can build a model that divides all people into several disjoint sets, and the people in each set are relatives of each other. In order to judge whether two people are relatives, it is only necessary to see whether they belong to the same set. Therefore, here you can consider Disjoint Set for maintenance. 11 | 12 | ## Introduction 13 | 14 | The important idea of Disjoint Set is to use an element in the set to represent this set. I have seen an interesting metaphor that compares the set to a gang, and the representative element is the gang boss. Next, let's use this metaphor to see how Disjoint Set works. 15 | 16 |
17 | 18 |
19 | 20 | In the beginning, all the gangsters fought on their own. Every gang boss is naturally themselve. (For a set with only one element, the representative element is naturally the only element) 21 | 22 | Now No. 1 and No. 3 are fighting, assuming that No. 1 wins (here it is not important who wins), then No. 3 will recognize No. 1 as the gang boss (merge the sets that No. 1 and No. 3 belong to, and No. 1 is the representative element). 23 | 24 | 25 |
26 |
27 | 28 | Now No. 2 wants to fight with No. 3 (merge the sets that No. 3 and No. 2 belong to), but No. 3 says, "don't fight with me, I'll call my boss to f**k you" (merge representative elements). It might as well assume that No. 1 won again this time, then No. 2 also recognizes No. 1 as the boss. 29 | 30 |
31 |
32 | 33 | Now let's assume that the No. 4, No. 5 and No. 6 also carried out some gang mergers, and the situation in the streets becomes as follows: 34 | 35 |
36 |
37 | 38 | Now suppose that No. 2 wants to fight with No. 6, just like what I just said, they call their boss No. 1 and No. 4 to come out and fight (it's never easy being the boss). After No. 1 wins, No. 4 recognizes No. 1 as the gang boss, and of course his subordinates also surrendered. 39 | 40 |
41 |
42 | 43 | Well, the metaphor is over. If you have a little graph theory foundation, I believe you have already noticed that this is a **tree** structure. To find the representative element of the set, you only need to find the parent node layer by layer (the circle pointed by the arrow in the figure). Go to the root node of the tree (the orange circle in the figure). The parent node of the root node is itself. We can draw it directly as a tree: 44 | 45 |
46 |
47 | 48 | In this way, we can write the simplest version code of Disjoint Set. 49 | 50 | ### Initialization 51 | 52 | ```c 53 | int fa[MAXN]; 54 | inline void init(int n) 55 | { 56 | for (int i = 1; i <= n; ++i) 57 | fa[i] = i; 58 | } 59 | ``` 60 | 61 | If there are n elements numbered 1, 2, 3, ..., n, we use an array fa[] to store the parent node of each element (because each element has and only one parent node, so this is feasible). At the beginning, we first set their parent node to ourselves. 62 | 63 | ### Query 64 | 65 | ```c 66 | int find(int x) 67 | { 68 | if(fa[x] == x) 69 | return x; 70 | else 71 | return find(fa[x]); 72 | } 73 | ``` 74 | 75 | We use recursive writing to realize the query of representative elements: go to the parent node layer by layer until the root node (the sign of the root node is that the parent node is itself). To determine whether two elements belong to the same collection, you only need to check whether their root nodes are the same. 76 | 77 | ### Merge 78 | 79 | ```c 80 | inline void merge(int i, int j) 81 | { 82 | fa[find(i)] = find(j); 83 | } 84 | ``` 85 | 86 | The merging operation is also very simple, first find the representative elements of the two collections, and then set the parent node of the former to the latter. Of course, the parent node of the latter can also be set to the former, which is not important here. A more reasonable comparison is given at the end of this article. 87 | 88 | ## Path Compression 89 | 90 | The simplest version's efficiency is quite low. For example, consider the following scenario: 91 | 92 |
93 |
94 | 95 | Now we want to merge(2,3), so we find 1 from 2, fa[1]=3, so it becomes like this: 96 | 97 |
98 |
99 | 100 | Then we find another element 4, and need to execute merge(2,4): 101 | 102 |
103 |
104 | 105 | Find 1 from 2, then find 3, then fa[3]=4, so it becomes like this: 106 | 107 |
108 |
109 | 110 | You should have a feeling that this may form a long **chain** . As the chain gets longer, it will become more and more difficult for us to find the root node from the bottom. 111 | 112 | How to solve it? We can use **path compression** . Since we only care about the **root node** corresponding to an element, we hope that the path from each element to the root node is as short as possible, preferably only one step, like this: 113 | 114 |
115 |
116 | 117 | In fact, this is also very easy to achieve. As long as we **set the parent node of each node along the way as the root node** during the query process. We can save a lot of trouble the next time we look up. This is easy to implement in recursion. 118 | 119 | ### Merge (Path Compression) 120 | 121 | ```c 122 | int find(int x) 123 | { 124 | if(x == fa[x]) 125 | return x; 126 | else{ 127 | fa[x] = find(fa[x]); // set the parent node as the root node 128 | return fa[x]; // return parent node 129 | } 130 | } 131 | ``` 132 | 133 | The above code is often shortened to one line: 134 | 135 | ```c 136 | int find(int x) 137 | { 138 | return x == fa[x] ? x : (fa[x] = find(fa[x])); 139 | } 140 | ``` 141 | 142 | Note that the priority of the assignment operator `=` is not as high as that of the ternary operator `?:` , and parentheses are required here. 143 | 144 | After path compression's 'optimization, the time complexity of Disjoint Set is quite low, and most Union and Find problems of Disjoint Set can be solved. However, for some problems with strict time limit, we can further optimize. 145 | 146 | ## Merge by rank 147 | 148 | Some people may have a misunderstanding, thinking that after path compression and optimization, Disjoint Set is always a **star graph** (commonly known as a tree with only two layers). But in fact, since the path compression is only performed during the query, and only one path is compressed, the final structure of the Disjoint Set may still be relatively complicated. For example, now we have a more complex tree that needs to be merged with a single-element set: 149 | 150 |
151 |
152 | 153 | If we want to merge(7,8) at this time, if we can choose, should we set the parent node of 7 to 8, or set the parent node of 8 to 7? 154 | 155 | Of course it is the latter. Because if the parent node of 7 is set to 8, the **depth** of the tree (the length of the longest chain in the tree) will be deepened, and the distance from each element in the original tree to the root node will become longer. Then we look for the root node The path will be correspondingly longer. Although we have path compression, path compression also consumes time. On the contrary, Ssetting the parent node of 8 to 7 does not have this problem because it does not affect unrelated nodes. 156 | 157 |
158 |
159 | 160 | This inspires us: we should merge simple trees into complex trees, not the other way around. Because after merging in this way, the number of nodes whose distance to the root node becomes longer is relatively small. 161 | 162 | We use an array `rank[]` to record the depth of the tree corresponding to each root node (if it is not the root node, its rank is equivalent to the depth of the subtree with it as the root node). At the beginning, set the rank (rank) of all elements to 1. When merging, compare the two root nodes, and merge the one with the smaller rank to the one with the higher rank. 163 | 164 | If path compression and merge by rank are used together, the time complexity is close to $O(n)$ , but it is likely to destroy the accuracy of rank. 165 | 166 | ### Initialization (merge by rank) 167 | 168 | ```c 169 | inline void init(int n) 170 | { 171 | for (int i = 1; i <= n; ++i) 172 | { 173 | fa[i] = i; 174 | rank[i] = 1; 175 | } 176 | } 177 | ``` 178 | 179 | ### Merge (merge by rank) 180 | 181 | ```c 182 | inline void merge(int i, int j) 183 | { 184 | int x = find(i), y = find(j); // First find two root nodes 185 | if (rank[x] <= rank[y]) 186 | fa[x] = y; 187 | else 188 | fa[y] = x; 189 | if (rank[x] == rank[y] && x != y) 190 | rank[y]++; // If the depth is the same and the root node is different, the depth of the new root node +1 191 | } 192 | ``` 193 | 194 | Why is the depth the same, but the depth of the new root node needs to be +1? As shown in the figure below, we have two trees with a depth of 2, and now we need to merge(2,5): 195 | 196 |
197 |
198 | 199 | Here, the parent node of 2 is set to 5, or the parent node of 5 is set to 2, in fact, there is not much difference. We choose the former, so it becomes like this: 200 | 201 |
202 |
203 | 204 | Obviously the depth of the tree is increased by 1. Another way of merging also increases the depth of the tree by +1. 205 | 206 | ## Disjoint Set applications 207 | 208 | I first give the AC code for the Relative Problem: 209 | 210 | ```c 211 | #include 212 | #define MAXN 5005 213 | int fa[MAXN], rank[MAXN]; 214 | inline void init(int n) 215 | { 216 | for (int i = 1; i <= n; ++i) 217 | { 218 | fa[i] = i; 219 | rank[i] = 1; 220 | } 221 | } 222 | int find(int x) 223 | { 224 | return x == fa[x] ? x : (fa[x] = find(fa[x])); 225 | } 226 | inline void merge(int i, int j) 227 | { 228 | int x = find(i), y = find(j); 229 | if (rank[x] <= rank[y]) 230 | fa[x] = y; 231 | else 232 | fa[y] = x; 233 | if (rank[x] == rank[y] && x != y) 234 | rank[y]++; 235 | } 236 | int main() 237 | { 238 | int n, m, p, x, y; 239 | scanf("%d%d%d", &n, &m, &p); 240 | init(n); 241 | for (int i = 0; i < m; ++i) 242 | { 243 | scanf("%d%d", &x, &y); 244 | merge(x, y); 245 | } 246 | for (int i = 0; i < p; ++i) 247 | { 248 | scanf("%d%d", &x, &y); 249 | printf("%s\n", find(x) == find(y) ? "Yes" : "No"); 250 | } 251 | return 0; 252 | } 253 | ``` 254 | 255 | Next, let's look at a NOIP improvement group original question: [Cheese](https://www.luogu.com.cn/problem/P3958). 256 | 257 | Do you see the relationship between this question and Disjoint Set? 258 | 259 | This is the 2D version, the 3D version in the problem is similar. 260 | 261 | Take a look at the picture above, do you see some tricks? We divide all holes into several sets, and once two holes intersect or tangent, put them into the same set. 262 | 263 | We can also make 2 **special elements**, respectively representing **bottom** and **top**, if a hole is in contact with the bottom, put it in the same set as the element representing the bottom, The same goes for the top. In the end, you just need to see if **top and bottom are in the same set**. This can be achieved by Disjoint Set. Look at the code: 264 | 265 | ```c 266 | #include 267 | #include 268 | #define MAXN 1005 269 | typedef long long ll; 270 | int fa[MAXN], rank[MAXN]; 271 | ll X[MAXN], Y[MAXN], Z[MAXN]; 272 | inline bool next_to(ll x1, ll y1, ll z1, ll x2, ll y2, ll z2, ll r) 273 | { 274 | return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2) <= 4 * r * r; 275 | // Determine whether two holes are intersecting or tangent 276 | } 277 | inline void init(int n) 278 | { 279 | for (int i = 1; i <= n; ++i) 280 | { 281 | fa[i] = i; 282 | rank[i] = 1; 283 | } 284 | } 285 | int find(int x) 286 | { 287 | return x == fa[x] ? x : (fa[x] = find(fa[x])); 288 | } 289 | inline void merge(int i, int j) 290 | { 291 | int x = find(i), y = find(j); 292 | if (rank[x] <= rank[y]) 293 | fa[x] = y; 294 | else 295 | fa[y] = x; 296 | if (rank[x] == rank[y] && x != y) 297 | rank[y]++; 298 | } 299 | int main() 300 | { 301 | int T, n, h; 302 | ll r; 303 | scanf("%d", &T); 304 | for (int I = 0; I < T; ++I) 305 | { 306 | memset(X, 0, sizeof(X)); 307 | memset(Y, 0, sizeof(Y)); 308 | memset(Z, 0, sizeof(Z)); 309 | scanf("%d%d%lld", &n, &h, &r); 310 | init(n); 311 | fa[1001] = 1001; // Use 1001 to represent the bottom 312 | fa[1002] = 1002; // Use 1002 to represent the top 313 | for (int i = 1; i <= n; ++i) 314 | scanf("%lld%lld%lld", X + i, Y + i, Z + i); 315 | for (int i = 1; i <= n; ++i) 316 | { 317 | if (Z[i] <= r) 318 | merge(i, 1001); // The hole in contact with the bottom merges with the bottom 319 | if (Z[i] + r >= h) 320 | merge(i, 1002); // The hole in contact with the top merges with the top 321 | } 322 | for (int i = 1; i <= n; ++i) 323 | { 324 | for (int j = i + 1; j <= n; ++j) 325 | { 326 | if (next_to(X[i], Y[i], Z[i], X[j], Y[j], Z[j], r)) 327 | merge(i, j); // Traverse all holes, merge intersecting or tangent balls 328 | } 329 | } 330 | printf("%s\n", find(1001) == find(1002) ? "Yes" : "No"); 331 | } 332 | return 0; 333 | } 334 | ``` 335 | 336 | Because of the data range, a long long is required here. 337 | 338 | There are many applications of Disjoint Set, such as the Kruskal algorithm of the minimum spanning tree. I won't go into details here. All in all, any group management problem involving elements can be considered to use Disjoint Set for maintenance. 339 | --------------------------------------------------------------------------------