# Longest Palindromic Substring using Dynamic Programming

• Difficulty Level : Hard
• Last Updated : 27 Jan, 2023

Given a string, find the longest substring which is a palindrome.
Example:

Input: Given string :”forgeeksskeegfor”,
Output: “geeksskeeg”

Input: Given string :”Geeks”,
Output: “ee”

Recommended Practice

Method 1: Brute Force.
Approach: The simple approach is to check each substring whether the substring is a palindrome or not. To do this first, run three nested loops, the outer two loops pick all substrings one by one by fixing the corner characters, the inner loop checks whether the picked substring is palindrome or not.

Below is the implementation of the above approach:

## C++

 `// A C++ solution for longest palindrome` `#include ` `using` `namespace` `std;`   `// Function to print a substring str[low..high]` `void` `printSubStr(string str, ``int` `low, ``int` `high)` `{` `    ``for` `(``int` `i = low; i <= high; ++i)` `        ``cout << str[i];` `}`   `// This function prints the` `// longest palindrome substring` `// It also returns the length` `// of the longest palindrome` `int` `longestPalSubstr(string str)` `{` `    ``// get length of input string` `    ``int` `n = str.size();`   `    ``// All substrings of length 1` `    ``// are palindromes` `    ``int` `maxLength = 1, start = 0;`   `    ``// Nested loop to mark start and end index` `    ``for` `(``int` `i = 0; i < str.length(); i++) {` `        ``for` `(``int` `j = i; j < str.length(); j++) {` `            ``int` `flag = 1;`   `            ``// Check palindrome` `            ``for` `(``int` `k = 0; k < (j - i + 1) / 2; k++)` `                ``if` `(str[i + k] != str[j - k])` `                    ``flag = 0;`   `            ``// Palindrome` `            ``if` `(flag && (j - i + 1) > maxLength) {` `                ``start = i;` `                ``maxLength = j - i + 1;` `            ``}` `        ``}` `    ``}`   `    ``cout << ``"Longest palindrome substring is: "``;` `    ``printSubStr(str, start, start + maxLength - 1);`   `    ``// return length of LPS` `    ``return` `maxLength;` `}`   `// Driver Code` `int` `main()` `{` `    ``string str = ``"forgeeksskeegfor"``;` `    ``cout << ``"\nLength is: "` `         ``<< longestPalSubstr(str);` `    ``return` `0;` `}`

## Java

 `// A Java solution for longest palindrome` `import` `java.util.*;`   `class` `GFG{`   `// Function to print a subString str[low..high]` `static` `void` `printSubStr(String str, ``int` `low, ``int` `high)` `{` `    ``for` `(``int` `i = low; i <= high; ++i)` `        ``System.out.print(str.charAt(i));` `}`   `// This function prints the` `// longest palindrome subString` `// It also returns the length` `// of the longest palindrome` `static` `int` `longestPalSubstr(String str)` `{` `    ``// get length of input String` `    ``int` `n = str.length();`   `    ``// All subStrings of length 1` `    ``// are palindromes` `    ``int` `maxLength = ``1``, start = ``0``;`   `    ``// Nested loop to mark start and end index` `    ``for` `(``int` `i = ``0``; i < str.length(); i++) {` `        ``for` `(``int` `j = i; j < str.length(); j++) {` `            ``int` `flag = ``1``;`   `            ``// Check palindrome` `            ``for` `(``int` `k = ``0``; k < (j - i + ``1``) / ``2``; k++)` `                ``if` `(str.charAt(i + k) != str.charAt(j - k))` `                    ``flag = ``0``;`   `            ``// Palindrome` `            ``if` `(flag!=``0` `&& (j - i + ``1``) > maxLength) {` `                ``start = i;` `                ``maxLength = j - i + ``1``;` `            ``}` `        ``}` `    ``}`   `    ``System.out.print(``"Longest palindrome subString is: "``);` `    ``printSubStr(str, start, start + maxLength - ``1``);`   `    ``// return length of LPS` `    ``return` `maxLength;` `}`   `// Driver Code` `public` `static` `void` `main(String[] args)` `{` `    ``String str = ``"forgeeksskeegfor"``;` `    ``System.out.print(``"\nLength is: "` `         ``+ longestPalSubstr(str));` `}` `}`   `// This code is contributed by shikhasingrajput`

## Python3

 `# A Python3 solution for longest palindrome`   `# Function to print a subString str[low..high]` `def` `printSubStr(``str``, low, high):` `    `  `    ``for` `i ``in` `range``(low, high ``+` `1``):` `        ``print``(``str``[i], end ``=` `"")`   `# This function prints the` `# longest palindrome subString` `# It also returns the length` `# of the longest palindrome` `def` `longestPalSubstr(``str``):` `    `  `    ``# Get length of input String` `    ``n ``=` `len``(``str``)` `    `  `    ``# All subStrings of length 1` `    ``# are palindromes` `    ``maxLength ``=` `1` `    ``start ``=` `0` `    `  `    ``# Nested loop to mark start` `    ``# and end index` `    ``for` `i ``in` `range``(n):` `        ``for` `j ``in` `range``(i, n):` `            ``flag ``=` `1` `            `  `            ``# Check palindrome` `            ``for` `k ``in` `range``(``0``, ((j ``-` `i) ``/``/` `2``) ``+` `1``):` `                ``if` `(``str``[i ``+` `k] !``=` `str``[j ``-` `k]):` `                    ``flag ``=` `0`   `            ``# Palindrome` `            ``if` `(flag !``=` `0` `and` `(j ``-` `i ``+` `1``) > maxLength):` `                ``start ``=` `i` `                ``maxLength ``=` `j ``-` `i ``+` `1` `                `  `    ``print``(``"Longest palindrome subString is: "``, end ``=` `"")` `    ``printSubStr(``str``, start, start ``+` `maxLength ``-` `1``)`   `    ``# Return length of LPS` `    ``return` `maxLength`   `# Driver Code` `if` `__name__ ``=``=` `'__main__'``:`   `    ``str` `=` `"forgeeksskeegfor"` `    `  `    ``print``(``"\nLength is: "``, longestPalSubstr(``str``))`   `# This code is contributed by 29AjayKumar`

## C#

 `// A C# solution for longest palindrome` `using` `System;`   `class` `GFG{`   `// Function to print a subString str[low..high]` `static` `void` `printSubStr(String str, ``int` `low, ``int` `high)` `{` `    ``for` `(``int` `i = low; i <= high; ++i)` `        ``Console.Write(str[i]);` `}`   `// This function prints the` `// longest palindrome subString` `// It also returns the length` `// of the longest palindrome` `static` `int` `longestPalSubstr(String str)` `{` `    ``// get length of input String` `    ``int` `n = str.Length;`   `    ``// All subStrings of length 1` `    ``// are palindromes` `    ``int` `maxLength = 1, start = 0;`   `    ``// Nested loop to mark start and end index` `    ``for` `(``int` `i = 0; i < str.Length; i++) {` `        ``for` `(``int` `j = i; j < str.Length; j++) {` `            ``int` `flag = 1;`   `            ``// Check palindrome` `            ``for` `(``int` `k = 0; k < (j - i + 1) / 2; k++)` `                ``if` `(str[i + k] != str[j - k])` `                    ``flag = 0;`   `            ``// Palindrome` `            ``if` `(flag!=0 && (j - i + 1) > maxLength) {` `                ``start = i;` `                ``maxLength = j - i + 1;` `            ``}` `        ``}` `    ``}`   `    ``Console.Write(``"longest palindrome subString is: "``);` `    ``printSubStr(str, start, start + maxLength - 1);`   `    ``// return length of LPS` `    ``return` `maxLength;` `}`   `// Driver Code` `public` `static` `void` `Main(String[] args)` `{` `    ``String str = ``"forgeeksskeegfor"``;` `    ``Console.Write(``"\nLength is: "` `         ``+ longestPalSubstr(str));` `}` `}`   `// This code is contributed by shikhasingrajput`

## Javascript

 ``

Output

```Longest palindrome substring is: geeksskeeg
Length is: 10```

Complexity Analysis:

• Time complexity: O(n^3).
Three nested loops are needed to find the longest palindromic substring in this approach, so the time complexity is O(n^3).
• Auxiliary complexity: O(1).
As no extra space is needed.

Method 2: Dynamic Programming.
Approach: The time complexity can be reduced by storing results of sub-problems.

1. Maintain a boolean table[n][n] that is filled in bottom up manner.
2. The value of table[i][j] is true, if the substring is palindrome, otherwise false.
3. To calculate table[i][j], check the value of table[i+1][j-1], if the value is true and str[i] is same as str[j], then we make table[i][j] true.
4. Otherwise, the value of table[i][j] is made false.
5. We have to fill table previously for substring of length = 1 and length =2 because
as we are finding , if table[i+1][j-1] is true or false , so in case of
(i) length == 1 , lets say i=2 , j=2 and i+1,j-1 doesn’t lies between [i , j]
(ii) length == 2 ,lets say i=2 , j=3 and i+1,j-1 again doesn’t lies between [i , j].

Below is the implementation of the above approach:

## C++

 `// A C++ dynamic programming` `// solution for longest palindrome`   `#include ` `using` `namespace` `std;`   `// Function to print a substring` `// str[low..high]` `void` `printSubStr(` `    ``string str, ``int` `low, ``int` `high)` `{` `    ``for` `(``int` `i = low; i <= high; ++i)` `        ``cout << str[i];` `}`   `// This function prints the` `// longest palindrome substring` `// It also returns the length of` `// the longest palindrome` `int` `longestPalSubstr(string str)` `{` `    ``// get length of input string` `    ``int` `n = str.size();`   `    ``// table[i][j] will be false if substring` `    ``// str[i..j] is not palindrome.` `    ``// Else table[i][j] will be true` `    ``bool` `table[n][n];`   `    ``memset``(table, 0, ``sizeof``(table));`   `    ``// All substrings of length 1` `    ``// are palindromes` `    ``int` `maxLength = 1;`   `    ``for` `(``int` `i = 0; i < n; ++i)` `        ``table[i][i] = ``true``;`   `    ``// check for sub-string of length 2.` `    ``int` `start = 0;` `    ``for` `(``int` `i = 0; i < n - 1; ++i) {` `        ``if` `(str[i] == str[i + 1]) {` `            ``table[i][i + 1] = ``true``;` `            ``start = i;` `            ``maxLength = 2;` `        ``}` `    ``}`   `    ``// Check for lengths greater than 2.` `    ``// k is length of substring` `    ``for` `(``int` `k = 3; k <= n; ++k) {` `        ``// Fix the starting index` `        ``for` `(``int` `i = 0; i < n - k + 1; ++i) {` `            ``// Get the ending index of substring from` `            ``// starting index i and length k` `            ``int` `j = i + k - 1;`   `            ``// checking for sub-string from ith index to` `            ``// jth index if str[i+1] to str[j-1] is a` `            ``// palindrome` `            ``if` `(table[i + 1][j - 1] && str[i] == str[j]) {` `                ``table[i][j] = ``true``;`   `                ``if` `(k > maxLength) {` `                    ``start = i;` `                    ``maxLength = k;` `                ``}` `            ``}` `        ``}` `    ``}`   `    ``cout << ``"Longest palindrome substring is: "``;` `    ``printSubStr(str, start, start + maxLength - 1);`   `    ``// return length of LPS` `    ``return` `maxLength;` `}`   `// Driver Code` `int` `main()` `{` `    ``string str = ``"forgeeksskeegfor"``;` `    ``cout << ``"\nLength is: "` `         ``<< longestPalSubstr(str);` `    ``return` `0;` `}`

## Java

 `// Java Solution` `public` `class` `LongestPalinSubstring {` `    ``// A utility function to print` `    ``// a substring str[low..high]` `    ``static` `void` `printSubStr(` `        ``String str, ``int` `low, ``int` `high)` `    ``{` `        ``System.out.println(` `            ``str.substring(` `                ``low, high + ``1``));` `    ``}`   `    ``// This function prints the longest` `    ``// palindrome substring of str[].` `    ``// It also returns the length of the` `    ``// longest palindrome` `    ``static` `int` `longestPalSubstr(String str)` `    ``{` `        ``// get length of input string` `        ``int` `n = str.length();`   `        ``// table[i][j] will be false if` `        ``// substring str[i..j] is not palindrome.` `        ``// Else table[i][j] will be true` `        ``boolean` `table[][] = ``new` `boolean``[n][n];`   `        ``// All substrings of length 1 are palindromes` `        ``int` `maxLength = ``1``;` `        ``for` `(``int` `i = ``0``; i < n; ++i)` `            ``table[i][i] = ``true``;`   `        ``// check for sub-string of length 2.` `        ``int` `start = ``0``;` `        ``for` `(``int` `i = ``0``; i < n - ``1``; ++i) {` `            ``if` `(str.charAt(i) == str.charAt(i + ``1``)) {` `                ``table[i][i + ``1``] = ``true``;` `                ``start = i;` `                ``maxLength = ``2``;` `            ``}` `        ``}`   `        ``// Check for lengths greater than 2.` `        ``// k is length of substring` `        ``for` `(``int` `k = ``3``; k <= n; ++k) {`   `            ``// Fix the starting index` `            ``for` `(``int` `i = ``0``; i < n - k + ``1``; ++i) {` `                ``// Get the ending index of substring from` `                ``// starting index i and length k` `                ``int` `j = i + k - ``1``;`   `                ``// checking for sub-string from ith index to` `                ``// jth index if str.charAt(i+1) to` `                ``// str.charAt(j-1) is a palindrome` `                ``if` `(table[i + ``1``][j - ``1``]` `                    ``&& str.charAt(i) == str.charAt(j)) {` `                    ``table[i][j] = ``true``;`   `                    ``if` `(k > maxLength) {` `                        ``start = i;` `                        ``maxLength = k;` `                    ``}` `                ``}` `            ``}` `        ``}` `        ``System.out.print(``"Longest palindrome substring is; "``);` `        ``printSubStr(str, start,` `                    ``start + maxLength - ``1``);`   `        ``// return length of LPS` `        ``return` `maxLength;` `    ``}`   `    ``// Driver program to test above functions` `    ``public` `static` `void` `main(String[] args)` `    ``{`   `        ``String str = ``"forgeeksskeegfor"``;` `        ``System.out.println(``"Length is: "` `+ longestPalSubstr(str));` `    ``}` `}`   `// This code is contributed by Sumit Ghosh`

## Python

 `# Python program`   `import` `sys`   `# A utility function to print a` `# substring str[low..high]` `def` `printSubStr(st, low, high) :` `    ``sys.stdout.write(st[low : high ``+` `1``])` `    ``sys.stdout.flush()` `    ``return` `''`   `# This function prints the longest palindrome` `# substring of st[]. It also returns the length` `# of the longest palindrome` `def` `longestPalSubstr(st) :` `    ``n ``=` `len``(st) ``# get length of input string`   `    ``# table[i][j] will be false if substring ` `    ``# str[i..j] is not palindrome. Else ` `    ``# table[i][j] will be true` `    ``table ``=` `[[``0` `for` `x ``in` `range``(n)] ``for` `y` `                          ``in` `range``(n)] ` `    `  `    ``# All substrings of length 1 are` `    ``# palindromes` `    ``maxLength ``=` `1` `    ``i ``=` `0` `    ``while` `(i < n) :` `        ``table[i][i] ``=` `True` `        ``i ``=` `i ``+` `1` `    `  `    ``# check for sub-string of length 2.` `    ``start ``=` `0` `    ``i ``=` `0` `    ``while` `i < n ``-` `1` `:` `        ``if` `(st[i] ``=``=` `st[i ``+` `1``]) :` `            ``table[i][i ``+` `1``] ``=` `True` `            ``start ``=` `i` `            ``maxLength ``=` `2` `        ``i ``=` `i ``+` `1` `    `  `    ``# Check for lengths greater than 2. ` `    ``# k is length of substring` `    ``k ``=` `3` `    ``while` `k <``=` `n :` `        ``# Fix the starting index` `        ``i ``=` `0` `        ``while` `i < (n ``-` `k ``+` `1``) :` `            `  `            ``# Get the ending index of ` `            ``# substring from starting ` `            ``# index i and length k` `            ``j ``=` `i ``+` `k ``-` `1` `    `  `            ``# checking for sub-string from` `            ``# ith index to jth index if ` `            ``# st[i + 1] to st[(j-1)] is a ` `            ``# palindrome` `            ``if` `(table[i ``+` `1``][j ``-` `1``] ``and` `                      ``st[i] ``=``=` `st[j]) :` `                ``table[i][j] ``=` `True` `    `  `                ``if` `(k > maxLength) :` `                    ``start ``=` `i` `                    ``maxLength ``=` `k` `            ``i ``=` `i ``+` `1` `        ``k ``=` `k ``+` `1` `    ``print` `"Longest palindrome substring is: "``, printSubStr(st, start,` `                                               ``start ``+` `maxLength ``-` `1``)`   `    ``return` `maxLength ``# return length of LPS`     `# Driver program to test above functions` `st ``=` `"forgeeksskeegfor"` `l ``=` `longestPalSubstr(st)` `print` `"Length is:"``, l`   `# This code is contributed by Nikita Tiwari.`

## C#

 `// C# Solution` `using` `System;`   `class` `GFG {`   `    ``// A utility function to print a` `    ``// substring str[low...( high - (low+1))]` `    ``static` `void` `printSubStr(``string` `str, ``int` `low,` `                            ``int` `high)` `    ``{` `        ``Console.WriteLine(str.Substring(low,` `                                        ``high - low + 1));` `    ``}`   `    ``// This function prints the longest` `    ``// palindrome substring of str[].` `    ``// It also returns the length of the` `    ``// longest palindrome` `    ``static` `int` `longestPalSubstr(``string` `str)` `    ``{`   `        ``// Get length of input string` `        ``int` `n = str.Length;`   `        ``// Table[i, j] will be false if substring` `        ``// str[i..j] is not palindrome. Else` `        ``// table[i, j] will be true` `        ``bool``[, ] table = ``new` `bool``[n, n];`   `        ``// All substrings of length 1 are palindromes` `        ``int` `maxLength = 1;` `        ``for` `(``int` `i = 0; i < n; ++i)` `            ``table[i, i] = ``true``;`   `        ``// Check for sub-string of length 2.` `        ``int` `start = 0;`   `        ``for` `(``int` `i = 0; i < n - 1; ++i) {` `            ``if` `(str[i] == str[i + 1]) {` `                ``table[i, i + 1] = ``true``;` `                ``start = i;` `                ``maxLength = 2;` `            ``}` `        ``}`   `        ``// Check for lengths greater than 2.` `        ``// k is length of substring` `        ``for` `(``int` `k = 3; k <= n; ++k) {`   `            ``// Fix the starting index` `            ``for` `(``int` `i = 0; i < n - k + 1; ++i) {`   `                ``// Get the ending index of substring from` `                ``// starting index i and length k` `                ``int` `j = i + k - 1;`   `                ``// Checking for sub-string from ith index` `                ``// to jth index if str.charAt(i+1) to` `                ``// str.charAt(j-1) is a palindrome` `                ``if` `(table[i + 1, j - 1] && str[i] == str[j]) {` `                    ``table[i, j] = ``true``;` `                    ``if` `(k > maxLength) {` `                        ``start = i;` `                        ``maxLength = k;` `                    ``}` `                ``}` `            ``}` `        ``}` `        ``Console.Write(``"Longest palindrome substring is: "``);` `        ``printSubStr(str, start, start + maxLength - 1);`   `        ``// Return length of LPS` `        ``return` `maxLength;` `    ``}`   `    ``// Driver code` `    ``public` `static` `void` `Main(``string``[] args)` `    ``{` `        ``string` `str = ``"forgeeksskeegfor"``;`   `        ``Console.WriteLine(``"Length is: "` `+ longestPalSubstr(str));` `    ``}` `}`   `// This code is contributed by SoumikMondal`

## Javascript

 ``

Output

```Longest palindrome substring is: geeksskeeg
Length is: 10```

Complexity Analysis:

• Time complexity: O(n^2).
Two nested traversals are needed.
• Auxiliary Space: O(n^2).
Matrix of size n*n is needed to store the dp array.

METHOD 3: Using loops

APPROACH: First we will run a loop for iterating every character. Then we will run another loop inside it to check that is there any other character similar to the current character. If it is, then it is possible that they both are first and last character of longest substring. We will store that substring and check whether that substring is longest or not. If yes then we will store that substring and keep iterating.

## C++

 `// A C++ solution for longest palindrome` `#include ` `using` `namespace` `std;` `// This function prints the` `// longest palindrome substring` `// It also returns the length` `// of the longest palindrome` `int` `longestPalSubstr(string str)` `{` `    ``//Stores Longest Pallidrome Substring` `    ``string longest = ``""``;` `    ``int` `n = str.length();` `    ``int` `j;` `    ``//To store substring which we think can be a pallindrome` `    ``string subs = ``""``;` `    ``//To strore reverse of substring we think can be pallidrome` `    ``string subsrev = ``""``;` `    ``for``(``int` `i = 0; i < n; i++){` `        ``j = n-1;` `        ``while``(i < j){` `            ``//Checking whether the character at i and j are same. If they are same then that substring can be LPS` `            ``if``((str[i] == str[j]) && (longest.length() < (j-i+1))){` `               ``subs = str.substr(i,(j-i+1));` `               ``//cout<

## Python3

 `# Python code for the above approach` `def` `longestPalSubstr(s):` `    ``n ``=` `len``(s)` `    `  `    ``# Stores Longest Pallidrome Substring` `    ``longest ``=` `""` `    ``j ``=` `0` `    ``# To store substring which we think can be a pallindrome` `    ``subs ``=` `""` `    ``# To strore reverse of substring we think can be pallidrome` `    ``subsrev ``=` `""` `    ``for` `i ``in` `range``(n):` `        ``j ``=` `n``-``1` `        ``while` `i < j:` `            ``# Checking whether the character ` `            ``# at i and j are same. If they are ` `            ``# same then that substring can be LPS` `            ``if``(s[i] ``=``=` `s[j] ``and` `len``(longest) < (j``-``i``+``1``)):` `               ``subs ``=` `s[i:(j``+``1``)]` `               ``subsrev ``=` `subs[::``-``1``]` `               ``if``(subs ``=``=` `subsrev):` `                  ``longest ``=` `subs` `            ``j ``-``=` `1` `            `  `    ``# If no longest substring then we will ` `    ``# return first character(In Leetcode it was a testcase so...)` `    ``if``(``len``(longest) ``=``=` `0``):` `        ``longest ``=` `s[``0``]` `    ``print``(``"Longest palindrome substring is: "` `+` `longest)` `    `  `    ``# return length of LPS` `    ``return` `len``(longest)`   `# Driver Code` `stri ``=` `"forgeeksskeegfor"` `print``(``"Length is: "` `+` `str``(longestPalSubstr(stri)))`   `# This code is contributed by lokeshpotta20.`

## Javascript

 `// A javascript solution for longest palindrome`   `// This function prints the` `// longest palindrome substring` `// It also returns the length` `// of the longest palindrome` `function` `longestPalSubstr(str)` `{` `    ``//Stores Longest Pallidrome Substring` `    ``let longest = ``""``;` `    ``let n = str.length;` `    ``let j;` `    ``//To store substring which we think can be a pallindrome` `    ``let subs = ``""``;` `    ``//To strore reverse of substring we think can be pallidrome` `    ``let subsrev = ``""``;` `    ``for``(let i = 0; i < n; i++){` `        ``j = n-1;` `        ``while``(i < j){` `            ``//Checking whether the character at i and j are same. If they are same then that substring can be LPS` `            ``if``((str[i] == str[j]) && (longest.length < (j-i+1))){` `               ``subs = str.substring(i,(j+1));` `               ``subsrev = subs;` `               ``subsrev=subsrev.split(``''``).reverse().join(``''``);` `               ``if``(subs == subsrev){` `                  ``longest = subs;` `               ``}` `            ``}` `            ``j--;` `        ``}` `    ``}` `   ``//If no longest substring then we will return ` `   ``// first character(In Leetcode it was a testcase so...)` `    ``if``(longest.length == 0){` `        ``longest = str[0];` `    ``}` `    ``console.log(``"Longest palindrome substring is: "` `+ longest);`   `    ``// return length of LPS` `    ``return` `longest.length;` `}`   `// Driver Code` `let str = ``"forgeeksskeegfor"``;` `console.log(``"Length is: "``+ longestPalSubstr(str));`

Output

```Longest palindrome substring is: geeksskeeg
Length is: 10```

Time and Space Complexity Analysis:

Time Complexity – Worst case -> O(n^3) because used 2 nested loops and a reverse function in 2nd nested loop.

But better than first solution because we were checking palindrome for all substrings but here we are checking for some cases only.

Auxiliary Space – O(3*(length of longest palindrome))  – To store answer, substring which we think can be palindrome and reverse of string which we think is a palindrome.

My Personal Notes arrow_drop_up
Related Articles